<html>
<head>
    <meta charset="utf-8">
    <title>SparkSQL</title>
    <style>
        #wrapper {
            width: 960px;
            margin: 0 auto;
        }

        /* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
        /* Uncomment @import statement below to use as custom stylesheet */
        /*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
        article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary {
            display: block
        }

        audio, canvas, video {
            display: inline-block
        }

        audio:not([controls]) {
            display: none;
            height: 0
        }

        script {
            display: none !important
        }

        html {
            font-family: sans-serif;
            -ms-text-size-adjust: 100%;
            -webkit-text-size-adjust: 100%
        }

        a {
            background: transparent
        }

        a:focus {
            outline: thin dotted
        }

        a:active, a:hover {
            outline: 0
        }

        h1 {
            font-size: 2em;
            margin: .67em 0
        }

        abbr[title] {
            border-bottom: 1px dotted
        }

        b, strong {
            font-weight: bold
        }

        dfn {
            font-style: italic
        }

        hr {
            -moz-box-sizing: content-box;
            box-sizing: content-box;
            height: 0
        }

        mark {
            background: #ff0;
            color: #000
        }

        code, kbd, pre, samp {
            font-family: monospace;
            font-size: 1em
        }

        pre {
            white-space: pre-wrap
        }

        q {
            quotes: "\201C" "\201D" "\2018" "\2019"
        }

        small {
            font-size: 80%
        }

        sub, sup {
            font-size: 75%;
            line-height: 0;
            position: relative;
            vertical-align: baseline
        }

        sup {
            top: -.5em
        }

        sub {
            bottom: -.25em
        }

        img {
            border: 0
        }

        svg:not(:root) {
            overflow: hidden
        }

        figure {
            margin: 0
        }

        fieldset {
            border: 1px solid silver;
            margin: 0 2px;
            padding: .35em .625em .75em
        }

        legend {
            border: 0;
            padding: 0
        }

        button, input, select, textarea {
            font-family: inherit;
            font-size: 100%;
            margin: 0
        }

        button, input {
            line-height: normal
        }

        button, select {
            text-transform: none
        }

        button, html input[type="button"], input[type="reset"], input[type="submit"] {
            -webkit-appearance: button;
            cursor: pointer
        }

        button[disabled], html input[disabled] {
            cursor: default
        }

        input[type="checkbox"], input[type="radio"] {
            box-sizing: border-box;
            padding: 0
        }

        button::-moz-focus-inner, input::-moz-focus-inner {
            border: 0;
            padding: 0
        }

        textarea {
            overflow: auto;
            vertical-align: top
        }

        table {
            border-collapse: collapse;
            border-spacing: 0
        }

        *, *::before, *::after {
            -moz-box-sizing: border-box;
            -webkit-box-sizing: border-box;
            box-sizing: border-box
        }

        html, body {
            font-size: 100%
        }

        body {
            background: #fff;
            color: rgba(0, 0, 0, .8);
            padding: 0;
            margin: 0;
            font-family: "Noto Serif", "DejaVu Serif", serif;
            font-weight: 400;
            font-style: normal;
            line-height: 1;
            position: relative;
            cursor: auto;
            tab-size: 4;
            -moz-osx-font-smoothing: grayscale;
            -webkit-font-smoothing: antialiased
        }

        a:hover {
            cursor: pointer
        }

        img, object, embed {
            max-width: 100%;
            height: auto
        }

        object, embed {
            height: 100%
        }

        img {
            -ms-interpolation-mode: bicubic
        }

        .left {
            float: left !important
        }

        .right {
            float: right !important
        }

        .text-left {
            text-align: left !important
        }

        .text-right {
            text-align: right !important
        }

        .text-center {
            text-align: center !important
        }

        .text-justify {
            text-align: justify !important
        }

        .hide {
            display: none
        }

        img, object, svg {
            display: inline-block;
            vertical-align: middle
        }

        textarea {
            height: auto;
            min-height: 50px
        }

        select {
            width: 100%
        }

        .center {
            margin-left: auto;
            margin-right: auto
        }

        .stretch {
            width: 100%
        }

        .subheader, .admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title {
            line-height: 1.45;
            color: #7a2518;
            font-weight: 400;
            margin-top: 0;
            margin-bottom: .25em
        }

        div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td {
            margin: 0;
            padding: 0;
            direction: ltr
        }

        a {
            color: #2156a5;
            text-decoration: underline;
            line-height: inherit
        }

        a:hover, a:focus {
            color: #1d4b8f
        }

        a img {
            border: none
        }

        p {
            font-family: inherit;
            font-weight: 400;
            font-size: 1em;
            line-height: 1.6;
            margin-bottom: 1.25em;
            text-rendering: optimizeLegibility
        }

        p aside {
            font-size: .875em;
            line-height: 1.35;
            font-style: italic
        }

        h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 {
            font-family: "Open Sans", "DejaVu Sans", sans-serif;
            font-weight: 300;
            font-style: normal;
            color: #ba3925;
            text-rendering: optimizeLegibility;
            margin-top: 1em;
            margin-bottom: .5em;
            line-height: 1.0125em
        }

        h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small {
            font-size: 60%;
            color: #e99b8f;
            line-height: 0
        }

        h1 {
            font-size: 2.125em
        }

        h2 {
            font-size: 1.6875em
        }

        h3, #toctitle, .sidebarblock > .content > .title {
            font-size: 1.375em
        }

        h4, h5 {
            font-size: 1.125em
        }

        h6 {
            font-size: 1em
        }

        hr {
            border: solid #dddddf;
            border-width: 1px 0 0;
            clear: both;
            margin: 1.25em 0 1.1875em;
            height: 0
        }

        em, i {
            font-style: italic;
            line-height: inherit
        }

        strong, b {
            font-weight: bold;
            line-height: inherit
        }

        small {
            font-size: 60%;
            line-height: inherit
        }

        code {
            font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace;
            font-weight: 400;
            color: rgba(0, 0, 0, .9)
        }

        ul, ol, dl {
            font-size: 1em;
            line-height: 1.6;
            margin-bottom: 1.25em;
            list-style-position: outside;
            font-family: inherit
        }

        ul, ol {
            margin-left: 1.5em
        }

        ul li ul, ul li ol {
            margin-left: 1.25em;
            margin-bottom: 0;
            font-size: 1em
        }

        ul.square li ul, ul.circle li ul, ul.disc li ul {
            list-style: inherit
        }

        ul.square {
            list-style-type: square
        }

        ul.circle {
            list-style-type: circle
        }

        ul.disc {
            list-style-type: disc
        }

        ol li ul, ol li ol {
            margin-left: 1.25em;
            margin-bottom: 0
        }

        dl dt {
            margin-bottom: .3125em;
            font-weight: bold
        }

        dl dd {
            margin-bottom: 1.25em
        }

        abbr, acronym {
            text-transform: uppercase;
            font-size: 90%;
            color: rgba(0, 0, 0, .8);
            border-bottom: 1px dotted #ddd;
            cursor: help
        }

        abbr {
            text-transform: none
        }

        blockquote {
            margin: 0 0 1.25em;
            padding: .5625em 1.25em 0 1.1875em;
            border-left: 1px solid #ddd
        }

        blockquote cite {
            display: block;
            font-size: .9375em;
            color: rgba(0, 0, 0, .6)
        }

        blockquote cite::before {
            content: "\2014 \0020"
        }

        blockquote cite a, blockquote cite a:visited {
            color: rgba(0, 0, 0, .6)
        }

        blockquote, blockquote p {
            line-height: 1.6;
            color: rgba(0, 0, 0, .85)
        }

        @media screen and (min-width: 768px) {
            h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 {
                line-height: 1.2
            }

            h1 {
                font-size: 2.75em
            }

            h2 {
                font-size: 2.3125em
            }

            h3, #toctitle, .sidebarblock > .content > .title {
                font-size: 1.6875em
            }

            h4 {
                font-size: 1.4375em
            }
        }

        table {
            background: #fff;
            margin-bottom: 1.25em;
            border: solid 1px #dedede
        }

        table thead, table tfoot {
            background: #f7f8f7
        }

        table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td {
            padding: .5em .625em .625em;
            font-size: inherit;
            color: rgba(0, 0, 0, .8);
            text-align: left
        }

        table tr th, table tr td {
            padding: .5625em .625em;
            font-size: inherit;
            color: rgba(0, 0, 0, .8)
        }

        table tr.even, table tr.alt, table tr:nth-of-type(even) {
            background: #f8f8f7
        }

        table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td {
            display: table-cell;
            line-height: 1.6
        }

        h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 {
            line-height: 1.2;
            word-spacing: -.05em
        }

        h1 strong, h2 strong, h3 strong, #toctitle strong, .sidebarblock > .content > .title strong, h4 strong, h5 strong, h6 strong {
            font-weight: 400
        }

        .clearfix::before, .clearfix::after, .float-group::before, .float-group::after {
            content: " ";
            display: table
        }

        .clearfix::after, .float-group::after {
            clear: both
        }

        *:not(pre) > code {
            font-size: .9375em;
            font-style: normal !important;
            letter-spacing: 0;
            padding: .1em .5ex;
            word-spacing: -.15em;
            background-color: #f7f7f8;
            -webkit-border-radius: 4px;
            border-radius: 4px;
            line-height: 1.45;
            text-rendering: optimizeSpeed;
            word-wrap: break-word
        }

        *:not(pre) > code.nobreak {
            word-wrap: normal
        }

        *:not(pre) > code.nowrap {
            white-space: nowrap
        }

        pre, pre > code {
            line-height: 1.45;
            color: rgba(0, 0, 0, .9);
            font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace;
            font-weight: 400;
            text-rendering: optimizeSpeed
        }

        em em {
            font-style: normal
        }

        strong strong {
            font-weight: 400
        }

        .keyseq {
            color: rgba(51, 51, 51, .8)
        }

        kbd {
            font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace;
            display: inline-block;
            color: rgba(0, 0, 0, .8);
            font-size: .65em;
            line-height: 1.45;
            background-color: #f7f7f7;
            border: 1px solid #ccc;
            -webkit-border-radius: 3px;
            border-radius: 3px;
            -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em white inset;
            box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em #fff inset;
            margin: 0 .15em;
            padding: .2em .5em;
            vertical-align: middle;
            position: relative;
            top: -.1em;
            white-space: nowrap
        }

        .keyseq kbd:first-child {
            margin-left: 0
        }

        .keyseq kbd:last-child {
            margin-right: 0
        }

        .menuseq, .menuref {
            color: #000
        }

        .menuseq b:not(.caret), .menuref {
            font-weight: inherit
        }

        .menuseq {
            word-spacing: -.02em
        }

        .menuseq b.caret {
            font-size: 1.25em;
            line-height: .8
        }

        .menuseq i.caret {
            font-weight: bold;
            text-align: center;
            width: .45em
        }

        b.button::before, b.button::after {
            position: relative;
            top: -1px;
            font-weight: 400
        }

        b.button::before {
            content: "[";
            padding: 0 3px 0 2px
        }

        b.button::after {
            content: "]";
            padding: 0 2px 0 3px
        }

        p a > code:hover {
            color: rgba(0, 0, 0, .9)
        }

        #header, #content, #footnotes, #footer {
            width: 100%;
            margin-left: auto;
            margin-right: auto;
            margin-top: 0;
            margin-bottom: 0;
            max-width: 62.5em;
            *zoom: 1;
            position: relative;
            padding-left: .9375em;
            padding-right: .9375em
        }

        #header::before, #header::after, #content::before, #content::after, #footnotes::before, #footnotes::after, #footer::before, #footer::after {
            content: " ";
            display: table
        }

        #header::after, #content::after, #footnotes::after, #footer::after {
            clear: both
        }

        #content {
            margin-top: 1.25em
        }

        #content::before {
            content: none
        }

        #header > h1:first-child {
            color: rgba(0, 0, 0, .85);
            margin-top: 2.25rem;
            margin-bottom: 0
        }

        #header > h1:first-child + #toc {
            margin-top: 8px;
            border-top: 1px solid #dddddf
        }

        #header > h1:only-child, body.toc2 #header > h1:nth-last-child(2) {
            border-bottom: 1px solid #dddddf;
            padding-bottom: 8px
        }

        #header .details {
            border-bottom: 1px solid #dddddf;
            line-height: 1.45;
            padding-top: .25em;
            padding-bottom: .25em;
            padding-left: .25em;
            color: rgba(0, 0, 0, .6);
            display: -ms-flexbox;
            display: -webkit-flex;
            display: flex;
            -ms-flex-flow: row wrap;
            -webkit-flex-flow: row wrap;
            flex-flow: row wrap
        }

        #header .details span:first-child {
            margin-left: -.125em
        }

        #header .details span.email a {
            color: rgba(0, 0, 0, .85)
        }

        #header .details br {
            display: none
        }

        #header .details br + span::before {
            content: "\00a0\2013\00a0"
        }

        #header .details br + span.author::before {
            content: "\00a0\22c5\00a0";
            color: rgba(0, 0, 0, .85)
        }

        #header .details br + span#revremark::before {
            content: "\00a0|\00a0"
        }

        #header #revnumber {
            text-transform: capitalize
        }

        #header #revnumber::after {
            content: "\00a0"
        }

        #content > h1:first-child:not([class]) {
            color: rgba(0, 0, 0, .85);
            border-bottom: 1px solid #dddddf;
            padding-bottom: 8px;
            margin-top: 0;
            padding-top: 1rem;
            margin-bottom: 1.25rem
        }

        #toc {
            border-bottom: 1px solid #e7e7e9;
            padding-bottom: .5em
        }

        #toc > ul {
            margin-left: .125em
        }

        #toc ul.sectlevel0 > li > a {
            font-style: italic
        }

        #toc ul.sectlevel0 ul.sectlevel1 {
            margin: .5em 0
        }

        #toc ul {
            font-family: "Open Sans", "DejaVu Sans", sans-serif;
            list-style-type: none
        }

        #toc li {
            line-height: 1.3334;
            margin-top: .3334em
        }

        #toc a {
            text-decoration: none
        }

        #toc a:active {
            text-decoration: underline
        }

        #toctitle {
            color: #7a2518;
            font-size: 1.2em
        }

        @media screen and (min-width: 768px) {
            #toctitle {
                font-size: 1.375em
            }

            body.toc2 {
                padding-left: 15em;
                padding-right: 0
            }

            #toc.toc2 {
                margin-top: 0 !important;
                background-color: #f8f8f7;
                position: fixed;
                width: 15em;
                left: 0;
                top: 0;
                border-right: 1px solid #e7e7e9;
                border-top-width: 0 !important;
                border-bottom-width: 0 !important;
                z-index: 1000;
                padding: 1.25em 1em;
                height: 100%;
                overflow: auto
            }

            #toc.toc2 #toctitle {
                margin-top: 0;
                margin-bottom: .8rem;
                font-size: 1.2em
            }

            #toc.toc2 > ul {
                font-size: .9em;
                margin-bottom: 0
            }

            #toc.toc2 ul ul {
                margin-left: 0;
                padding-left: 1em
            }

            #toc.toc2 ul.sectlevel0 ul.sectlevel1 {
                padding-left: 0;
                margin-top: .5em;
                margin-bottom: .5em
            }

            body.toc2.toc-right {
                padding-left: 0;
                padding-right: 15em
            }

            body.toc2.toc-right #toc.toc2 {
                border-right-width: 0;
                border-left: 1px solid #e7e7e9;
                left: auto;
                right: 0
            }
        }

        @media screen and (min-width: 1280px) {
            body.toc2 {
                padding-left: 20em;
                padding-right: 0
            }

            #toc.toc2 {
                width: 20em
            }

            #toc.toc2 #toctitle {
                font-size: 1.375em
            }

            #toc.toc2 > ul {
                font-size: .95em
            }

            #toc.toc2 ul ul {
                padding-left: 1.25em
            }

            body.toc2.toc-right {
                padding-left: 0;
                padding-right: 20em
            }
        }

        #content #toc {
            border-style: solid;
            border-width: 1px;
            border-color: #e0e0dc;
            margin-bottom: 1.25em;
            padding: 1.25em;
            background: #f8f8f7;
            -webkit-border-radius: 4px;
            border-radius: 4px
        }

        #content #toc > :first-child {
            margin-top: 0
        }

        #content #toc > :last-child {
            margin-bottom: 0
        }

        #footer {
            max-width: 100%;
            background-color: rgba(0, 0, 0, .8);
            padding: 1.25em
        }

        #footer-text {
            color: rgba(255, 255, 255, .8);
            line-height: 1.44
        }

        #content {
            margin-bottom: .625em
        }

        .sect1 {
            padding-bottom: .625em
        }

        @media screen and (min-width: 768px) {
            #content {
                margin-bottom: 1.25em
            }

            .sect1 {
                padding-bottom: 1.25em
            }
        }

        .sect1:last-child {
            padding-bottom: 0
        }

        .sect1 + .sect1 {
            border-top: 1px solid #e7e7e9
        }

        #content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor {
            position: absolute;
            z-index: 1001;
            width: 1.5ex;
            margin-left: -1.5ex;
            display: block;
            text-decoration: none !important;
            visibility: hidden;
            text-align: center;
            font-weight: 400
        }

        #content h1 > a.anchor::before, h2 > a.anchor::before, h3 > a.anchor::before, #toctitle > a.anchor::before, .sidebarblock > .content > .title > a.anchor::before, h4 > a.anchor::before, h5 > a.anchor::before, h6 > a.anchor::before {
            content: "\00A7";
            font-size: .85em;
            display: block;
            padding-top: .1em
        }

        #content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover {
            visibility: visible
        }

        #content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link {
            color: #ba3925;
            text-decoration: none
        }

        #content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover {
            color: #a53221
        }

        .audioblock, .imageblock, .literalblock, .listingblock, .stemblock, .videoblock {
            margin-bottom: 1.25em
        }

        .admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title {
            text-rendering: optimizeLegibility;
            text-align: left;
            font-family: "Noto Serif", "DejaVu Serif", serif;
            font-size: 1rem;
            font-style: italic
        }

        table.tableblock.fit-content > caption.title {
            white-space: nowrap;
            width: 0
        }

        .paragraph.lead > p, #preamble > .sectionbody > [class="paragraph"]:first-of-type p {
            font-size: 1.21875em;
            line-height: 1.6;
            color: rgba(0, 0, 0, .85)
        }

        table.tableblock #preamble > .sectionbody > [class="paragraph"]:first-of-type p {
            font-size: inherit
        }

        .admonitionblock > table {
            border-collapse: separate;
            border: 0;
            background: none;
            width: 100%
        }

        .admonitionblock > table td.icon {
            text-align: center;
            width: 80px
        }

        .admonitionblock > table td.icon img {
            max-width: none
        }

        .admonitionblock > table td.icon .title {
            font-weight: bold;
            font-family: "Open Sans", "DejaVu Sans", sans-serif;
            text-transform: uppercase
        }

        .admonitionblock > table td.content {
            padding-left: 1.125em;
            padding-right: 1.25em;
            border-left: 1px solid #dddddf;
            color: rgba(0, 0, 0, .6)
        }

        .admonitionblock > table td.content > :last-child > :last-child {
            margin-bottom: 0
        }

        .exampleblock > .content {
            border-style: solid;
            border-width: 1px;
            border-color: #e6e6e6;
            margin-bottom: 1.25em;
            padding: 1.25em;
            background: #fff;
            -webkit-border-radius: 4px;
            border-radius: 4px
        }

        .exampleblock > .content > :first-child {
            margin-top: 0
        }

        .exampleblock > .content > :last-child {
            margin-bottom: 0
        }

        .sidebarblock {
            border-style: solid;
            border-width: 1px;
            border-color: #e0e0dc;
            margin-bottom: 1.25em;
            padding: 1.25em;
            background: #f8f8f7;
            -webkit-border-radius: 4px;
            border-radius: 4px
        }

        .sidebarblock > :first-child {
            margin-top: 0
        }

        .sidebarblock > :last-child {
            margin-bottom: 0
        }

        .sidebarblock > .content > .title {
            color: #7a2518;
            margin-top: 0;
            text-align: center
        }

        .exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child {
            margin-bottom: 0
        }

        .literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint {
            background: #f7f7f8
        }

        .sidebarblock .literalblock pre, .sidebarblock .listingblock pre:not(.highlight), .sidebarblock .listingblock pre[class="highlight"], .sidebarblock .listingblock pre[class^="highlight "], .sidebarblock .listingblock pre.CodeRay, .sidebarblock .listingblock pre.prettyprint {
            background: #f2f1f1
        }

        .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] {
            -webkit-border-radius: 4px;
            border-radius: 4px;
            word-wrap: break-word;
            overflow-x: auto;
            padding: 1em;
            font-size: .8125em
        }

        @media screen and (min-width: 768px) {
            .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] {
                font-size: .90625em
            }
        }

        @media screen and (min-width: 1280px) {
            .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] {
                font-size: 1em
            }
        }

        .literalblock pre.nowrap, .literalblock pre.nowrap pre, .listingblock pre.nowrap, .listingblock pre.nowrap pre {
            white-space: pre;
            word-wrap: normal
        }

        .literalblock.output pre {
            color: #f7f7f8;
            background-color: rgba(0, 0, 0, .9)
        }

        .listingblock pre.highlightjs {
            padding: 0
        }

        .listingblock pre.highlightjs > code {
            padding: 1em;
            -webkit-border-radius: 4px;
            border-radius: 4px
        }

        .listingblock pre.prettyprint {
            border-width: 0
        }

        .listingblock > .content {
            position: relative
        }

        .listingblock code[data-lang]::before {
            display: none;
            content: attr(data-lang);
            position: absolute;
            font-size: .75em;
            top: .425rem;
            right: .5rem;
            line-height: 1;
            text-transform: uppercase;
            color: #999
        }

        .listingblock:hover code[data-lang]::before {
            display: block
        }

        .listingblock.terminal pre .command::before {
            content: attr(data-prompt);
            padding-right: .5em;
            color: #999
        }

        .listingblock.terminal pre .command:not([data-prompt])::before {
            content: "$"
        }

        table.pyhltable {
            border-collapse: separate;
            border: 0;
            margin-bottom: 0;
            background: none
        }

        table.pyhltable td {
            vertical-align: top;
            padding-top: 0;
            padding-bottom: 0;
            line-height: 1.45
        }

        table.pyhltable td.code {
            padding-left: .75em;
            padding-right: 0
        }

        pre.pygments .lineno, table.pyhltable td:not(.code) {
            color: #999;
            padding-left: 0;
            padding-right: .5em;
            border-right: 1px solid #dddddf
        }

        pre.pygments .lineno {
            display: inline-block;
            margin-right: .25em
        }

        table.pyhltable .linenodiv {
            background: none !important;
            padding-right: 0 !important
        }

        .quoteblock {
            margin: 0 1em 1.25em 1.5em;
            display: table
        }

        .quoteblock > .title {
            margin-left: -1.5em;
            margin-bottom: .75em
        }

        .quoteblock blockquote, .quoteblock p {
            color: rgba(0, 0, 0, .85);
            font-size: 1.15rem;
            line-height: 1.75;
            word-spacing: .1em;
            letter-spacing: 0;
            font-style: italic;
            text-align: justify
        }

        .quoteblock blockquote {
            margin: 0;
            padding: 0;
            border: 0
        }

        .quoteblock blockquote::before {
            content: "\201c";
            float: left;
            font-size: 2.75em;
            font-weight: bold;
            line-height: .6em;
            margin-left: -.6em;
            color: #7a2518;
            text-shadow: 0 1px 2px rgba(0, 0, 0, .1)
        }

        .quoteblock blockquote > .paragraph:last-child p {
            margin-bottom: 0
        }

        .quoteblock .attribution {
            margin-top: .75em;
            margin-right: .5ex;
            text-align: right
        }

        .verseblock {
            margin: 0 1em 1.25em
        }

        .verseblock pre {
            font-family: "Open Sans", "DejaVu Sans", sans;
            font-size: 1.15rem;
            color: rgba(0, 0, 0, .85);
            font-weight: 300;
            text-rendering: optimizeLegibility
        }

        .verseblock pre strong {
            font-weight: 400
        }

        .verseblock .attribution {
            margin-top: 1.25rem;
            margin-left: .5ex
        }

        .quoteblock .attribution, .verseblock .attribution {
            font-size: .9375em;
            line-height: 1.45;
            font-style: italic
        }

        .quoteblock .attribution br, .verseblock .attribution br {
            display: none
        }

        .quoteblock .attribution cite, .verseblock .attribution cite {
            display: block;
            letter-spacing: -.025em;
            color: rgba(0, 0, 0, .6)
        }

        .quoteblock.abstract blockquote::before, .quoteblock.excerpt blockquote::before, .quoteblock .quoteblock blockquote::before {
            display: none
        }

        .quoteblock.abstract blockquote, .quoteblock.abstract p, .quoteblock.excerpt blockquote, .quoteblock.excerpt p, .quoteblock .quoteblock blockquote, .quoteblock .quoteblock p {
            line-height: 1.6;
            word-spacing: 0
        }

        .quoteblock.abstract {
            margin: 0 1em 1.25em;
            display: block
        }

        .quoteblock.abstract > .title {
            margin: 0 0 .375em;
            font-size: 1.15em;
            text-align: center
        }

        .quoteblock.excerpt, .quoteblock .quoteblock {
            margin: 0 0 1.25em;
            padding: 0 0 .25em 1em;
            border-left: .25em solid #dddddf
        }

        .quoteblock.excerpt blockquote, .quoteblock.excerpt p, .quoteblock .quoteblock blockquote, .quoteblock .quoteblock p {
            color: inherit;
            font-size: 1.0625rem
        }

        .quoteblock.excerpt .attribution, .quoteblock .quoteblock .attribution {
            color: inherit;
            text-align: left;
            margin-right: 0
        }

        table.tableblock {
            max-width: 100%;
            border-collapse: separate
        }

        p.tableblock:last-child {
            margin-bottom: 0
        }

        td.tableblock > .content {
            margin-bottom: -1.25em
        }

        table.tableblock, th.tableblock, td.tableblock {
            border: 0 solid #dedede
        }

        table.grid-all > thead > tr > .tableblock, table.grid-all > tbody > tr > .tableblock {
            border-width: 0 1px 1px 0
        }

        table.grid-all > tfoot > tr > .tableblock {
            border-width: 1px 1px 0 0
        }

        table.grid-cols > * > tr > .tableblock {
            border-width: 0 1px 0 0
        }

        table.grid-rows > thead > tr > .tableblock, table.grid-rows > tbody > tr > .tableblock {
            border-width: 0 0 1px
        }

        table.grid-rows > tfoot > tr > .tableblock {
            border-width: 1px 0 0
        }

        table.grid-all > * > tr > .tableblock:last-child, table.grid-cols > * > tr > .tableblock:last-child {
            border-right-width: 0
        }

        table.grid-all > tbody > tr:last-child > .tableblock, table.grid-all > thead:last-child > tr > .tableblock, table.grid-rows > tbody > tr:last-child > .tableblock, table.grid-rows > thead:last-child > tr > .tableblock {
            border-bottom-width: 0
        }

        table.frame-all {
            border-width: 1px
        }

        table.frame-sides {
            border-width: 0 1px
        }

        table.frame-topbot, table.frame-ends {
            border-width: 1px 0
        }

        table.stripes-all tr, table.stripes-odd tr:nth-of-type(odd) {
            background: #f8f8f7
        }

        table.stripes-none tr, table.stripes-odd tr:nth-of-type(even) {
            background: none
        }

        th.halign-left, td.halign-left {
            text-align: left
        }

        th.halign-right, td.halign-right {
            text-align: right
        }

        th.halign-center, td.halign-center {
            text-align: center
        }

        th.valign-top, td.valign-top {
            vertical-align: top
        }

        th.valign-bottom, td.valign-bottom {
            vertical-align: bottom
        }

        th.valign-middle, td.valign-middle {
            vertical-align: middle
        }

        table thead th, table tfoot th {
            font-weight: bold
        }

        tbody tr th {
            display: table-cell;
            line-height: 1.6;
            background: #f7f8f7
        }

        tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p {
            color: rgba(0, 0, 0, .8);
            font-weight: bold
        }

        p.tableblock > code:only-child {
            background: none;
            padding: 0
        }

        p.tableblock {
            font-size: 1em
        }

        td > div.verse {
            white-space: pre
        }

        ol {
            margin-left: 1.75em
        }

        ul li ol {
            margin-left: 1.5em
        }

        dl dd {
            margin-left: 1.125em
        }

        dl dd:last-child, dl dd:last-child > :last-child {
            margin-bottom: 0
        }

        ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist {
            margin-bottom: .625em
        }

        ul.checklist, ul.none, ol.none, ul.no-bullet, ol.no-bullet, ol.unnumbered, ul.unstyled, ol.unstyled {
            list-style-type: none
        }

        ul.no-bullet, ol.no-bullet, ol.unnumbered {
            margin-left: .625em
        }

        ul.unstyled, ol.unstyled {
            margin-left: 0
        }

        ul.checklist {
            margin-left: .625em
        }

        ul.checklist li > p:first-child > .fa-square-o:first-child, ul.checklist li > p:first-child > .fa-check-square-o:first-child {
            width: 1.25em;
            font-size: .8em;
            position: relative;
            bottom: .125em
        }

        ul.checklist li > p:first-child > input[type="checkbox"]:first-child {
            margin-right: .25em
        }

        ul.inline {
            display: -ms-flexbox;
            display: -webkit-box;
            display: flex;
            -ms-flex-flow: row wrap;
            -webkit-flex-flow: row wrap;
            flex-flow: row wrap;
            list-style: none;
            margin: 0 0 .625em -1.25em
        }

        ul.inline > li {
            margin-left: 1.25em
        }

        .unstyled dl dt {
            font-weight: 400;
            font-style: normal
        }

        ol.arabic {
            list-style-type: decimal
        }

        ol.decimal {
            list-style-type: decimal-leading-zero
        }

        ol.loweralpha {
            list-style-type: lower-alpha
        }

        ol.upperalpha {
            list-style-type: upper-alpha
        }

        ol.lowerroman {
            list-style-type: lower-roman
        }

        ol.upperroman {
            list-style-type: upper-roman
        }

        ol.lowergreek {
            list-style-type: lower-greek
        }

        .hdlist > table, .colist > table {
            border: 0;
            background: none
        }

        .hdlist > table > tbody > tr, .colist > table > tbody > tr {
            background: none
        }

        td.hdlist1, td.hdlist2 {
            vertical-align: top;
            padding: 0 .625em
        }

        td.hdlist1 {
            font-weight: bold;
            padding-bottom: 1.25em
        }

        .literalblock + .colist, .listingblock + .colist {
            margin-top: -.5em
        }

        .colist td:not([class]):first-child {
            padding: .4em .75em 0;
            line-height: 1;
            vertical-align: top
        }

        .colist td:not([class]):first-child img {
            max-width: none
        }

        .colist td:not([class]):last-child {
            padding: .25em 0
        }

        .thumb, .th {
            line-height: 0;
            display: inline-block;
            border: solid 4px #fff;
            -webkit-box-shadow: 0 0 0 1px #ddd;
            box-shadow: 0 0 0 1px #ddd
        }

        .imageblock.left {
            margin: .25em .625em 1.25em 0
        }

        .imageblock.right {
            margin: .25em 0 1.25em .625em
        }

        .imageblock > .title {
            margin-bottom: 0
        }

        .imageblock.thumb, .imageblock.th {
            border-width: 6px
        }

        .imageblock.thumb > .title, .imageblock.th > .title {
            padding: 0 .125em
        }

        .image.left, .image.right {
            margin-top: .25em;
            margin-bottom: .25em;
            display: inline-block;
            line-height: 0
        }

        .image.left {
            margin-right: .625em
        }

        .image.right {
            margin-left: .625em
        }

        a.image {
            text-decoration: none;
            display: inline-block
        }

        a.image object {
            pointer-events: none
        }

        sup.footnote, sup.footnoteref {
            font-size: .875em;
            position: static;
            vertical-align: super
        }

        sup.footnote a, sup.footnoteref a {
            text-decoration: none
        }

        sup.footnote a:active, sup.footnoteref a:active {
            text-decoration: underline
        }

        #footnotes {
            padding-top: .75em;
            padding-bottom: .75em;
            margin-bottom: .625em
        }

        #footnotes hr {
            width: 20%;
            min-width: 6.25em;
            margin: -.25em 0 .75em;
            border-width: 1px 0 0
        }

        #footnotes .footnote {
            padding: 0 .375em 0 .225em;
            line-height: 1.3334;
            font-size: .875em;
            margin-left: 1.2em;
            margin-bottom: .2em
        }

        #footnotes .footnote a:first-of-type {
            font-weight: bold;
            text-decoration: none;
            margin-left: -1.05em
        }

        #footnotes .footnote:last-of-type {
            margin-bottom: 0
        }

        #content #footnotes {
            margin-top: -.625em;
            margin-bottom: 0;
            padding: .75em 0
        }

        .gist .file-data > table {
            border: 0;
            background: #fff;
            width: 100%;
            margin-bottom: 0
        }

        .gist .file-data > table td.line-data {
            width: 99%
        }

        div.unbreakable {
            page-break-inside: avoid
        }

        .big {
            font-size: larger
        }

        .small {
            font-size: smaller
        }

        .underline {
            text-decoration: underline
        }

        .overline {
            text-decoration: overline
        }

        .line-through {
            text-decoration: line-through
        }

        .aqua {
            color: #00bfbf
        }

        .aqua-background {
            background-color: #00fafa
        }

        .black {
            color: #000
        }

        .black-background {
            background-color: #000
        }

        .blue {
            color: #0000bf
        }

        .blue-background {
            background-color: #0000fa
        }

        .fuchsia {
            color: #bf00bf
        }

        .fuchsia-background {
            background-color: #fa00fa
        }

        .gray {
            color: #606060
        }

        .gray-background {
            background-color: #7d7d7d
        }

        .green {
            color: #006000
        }

        .green-background {
            background-color: #007d00
        }

        .lime {
            color: #00bf00
        }

        .lime-background {
            background-color: #00fa00
        }

        .maroon {
            color: #600000
        }

        .maroon-background {
            background-color: #7d0000
        }

        .navy {
            color: #000060
        }

        .navy-background {
            background-color: #00007d
        }

        .olive {
            color: #606000
        }

        .olive-background {
            background-color: #7d7d00
        }

        .purple {
            color: #600060
        }

        .purple-background {
            background-color: #7d007d
        }

        .red {
            color: #bf0000
        }

        .red-background {
            background-color: #fa0000
        }

        .silver {
            color: #909090
        }

        .silver-background {
            background-color: #bcbcbc
        }

        .teal {
            color: #006060
        }

        .teal-background {
            background-color: #007d7d
        }

        .white {
            color: #bfbfbf
        }

        .white-background {
            background-color: #fafafa
        }

        .yellow {
            color: #bfbf00
        }

        .yellow-background {
            background-color: #fafa00
        }

        span.icon > .fa {
            cursor: default
        }

        a span.icon > .fa {
            cursor: inherit
        }

        .admonitionblock td.icon [class^="fa icon-"] {
            font-size: 2.5em;
            text-shadow: 1px 1px 2px rgba(0, 0, 0, .5);
            cursor: default
        }

        .admonitionblock td.icon .icon-note::before {
            content: "\f05a";
            color: #19407c
        }

        .admonitionblock td.icon .icon-tip::before {
            content: "\f0eb";
            text-shadow: 1px 1px 2px rgba(155, 155, 0, .8);
            color: #111
        }

        .admonitionblock td.icon .icon-warning::before {
            content: "\f071";
            color: #bf6900
        }

        .admonitionblock td.icon .icon-caution::before {
            content: "\f06d";
            color: #bf3400
        }

        .admonitionblock td.icon .icon-important::before {
            content: "\f06a";
            color: #bf0000
        }

        .conum[data-value] {
            display: inline-block;
            color: #fff !important;
            background-color: rgba(0, 0, 0, .8);
            -webkit-border-radius: 100px;
            border-radius: 100px;
            text-align: center;
            font-size: .75em;
            width: 1.67em;
            height: 1.67em;
            line-height: 1.67em;
            font-family: "Open Sans", "DejaVu Sans", sans-serif;
            font-style: normal;
            font-weight: bold
        }

        .conum[data-value] * {
            color: #fff !important
        }

        .conum[data-value] + b {
            display: none
        }

        .conum[data-value]::after {
            content: attr(data-value)
        }

        pre .conum[data-value] {
            position: relative;
            top: -.125em
        }

        b.conum * {
            color: inherit !important
        }

        .conum:not([data-value]):empty {
            display: none
        }

        dt, th.tableblock, td.content, div.footnote {
            text-rendering: optimizeLegibility
        }

        h1, h2, p, td.content, span.alt {
            letter-spacing: -.01em
        }

        p strong, td.content strong, div.footnote strong {
            letter-spacing: -.005em
        }

        p, blockquote, dt, td.content, span.alt {
            font-size: 1.0625rem
        }

        p {
            margin-bottom: 1.25rem
        }

        .sidebarblock p, .sidebarblock dt, .sidebarblock td.content, p.tableblock {
            font-size: 1em
        }

        .exampleblock > .content {
            background-color: #fffef7;
            border-color: #e0e0dc;
            -webkit-box-shadow: 0 1px 4px #e0e0dc;
            box-shadow: 0 1px 4px #e0e0dc
        }

        .print-only {
            display: none !important
        }

        @page {
            margin: 1.25cm .75cm
        }

        @media print {
            * {
                -webkit-box-shadow: none !important;
                box-shadow: none !important;
                text-shadow: none !important
            }

            html {
                font-size: 80%
            }

            a {
                color: inherit !important;
                text-decoration: underline !important
            }

            a.bare, a[href^="#"], a[href^="mailto:"] {
                text-decoration: none !important
            }

            a[href^="http:"]:not(.bare)::after, a[href^="https:"]:not(.bare)::after {
                content: "(" attr(href) ")";
                display: inline-block;
                font-size: .875em;
                padding-left: .25em
            }

            abbr[title]::after {
                content: " (" attr(title) ")"
            }

            pre, blockquote, tr, img, object, svg {
                page-break-inside: avoid
            }

            thead {
                display: table-header-group
            }

            svg {
                max-width: 100%
            }

            p, blockquote, dt, td.content {
                font-size: 1em;
                orphans: 3;
                widows: 3
            }

            h2, h3, #toctitle, .sidebarblock > .content > .title {
                page-break-after: avoid
            }

            #toc, .sidebarblock, .exampleblock > .content {
                background: none !important
            }

            #toc {
                border-bottom: 1px solid #dddddf !important;
                padding-bottom: 0 !important
            }

            body.book #header {
                text-align: center
            }

            body.book #header > h1:first-child {
                border: 0 !important;
                margin: 2.5em 0 1em
            }

            body.book #header .details {
                border: 0 !important;
                display: block;
                padding: 0 !important
            }

            body.book #header .details span:first-child {
                margin-left: 0 !important
            }

            body.book #header .details br {
                display: block
            }

            body.book #header .details br + span::before {
                content: none !important
            }

            body.book #toc {
                border: 0 !important;
                text-align: left !important;
                padding: 0 !important;
                margin: 0 !important
            }

            body.book #toc, body.book #preamble, body.book h1.sect0, body.book .sect1 > h2 {
                page-break-before: always
            }

            .listingblock code[data-lang]::before {
                display: block
            }

            #footer {
                padding: 0 .9375em
            }

            .hide-on-print {
                display: none !important
            }

            .print-only {
                display: block !important
            }

            .hide-for-print {
                display: none !important
            }

            .show-for-print {
                display: inherit !important
            }
        }

        @media print, amzn-kf8 {
            #header > h1:first-child {
                margin-top: 1.25rem
            }

            .sect1 {
                padding: 0 !important
            }

            .sect1 + .sect1 {
                border: 0
            }

            #footer {
                background: none
            }

            #footer-text {
                color: rgba(0, 0, 0, .6);
                font-size: .9em
            }
        }

        @media amzn-kf8 {
            #header, #content, #footnotes, #footer {
                padding: 0
            }
        }

    </style>
    <link href='https://fonts.googleapis.com/css?family=Noto+Serif' rel='stylesheet' type='text/css'>
    <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic'
          rel='stylesheet' type='text/css'>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/styles/default.min.css" rel="stylesheet">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/highlight.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/asciidoc.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/yaml.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/dockerfile.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/makefile.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/go.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/rust.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/haskell.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/typescript.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/scss.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/less.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/handlebars.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/groovy.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/scala.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/bash.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.9.0/languages/ini.min.js"></script>
    <script>hljs.initHighlightingOnLoad();</script>
</head>
<body>
<div id="wrapper">
    <div class="article">
        <h1>SparkSQL</h1>
        <div id="preamble">
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">目标</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p><code>SparkSQL</code> 是什么</p>
                                </li>
                                <li>
                                    <p><code>SparkSQL</code> 如何使用</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
            </div>
            <div class="toc" id="toc">
                <div id="toctitle">Table of Contents</div>
                <ul class="sectlevel1">
                    <li><a href="#_1_sparksql_是什么">1. SparkSQL 是什么</a>
                        <ul class="sectlevel2">
                            <li><a href="#_1_1_sparksql_的出现契机">1.1. SparkSQL 的出现契机</a></li>
                            <li><a href="#_1_2_sparksql_的适用场景">1.2. SparkSQL 的适用场景</a></li>
                        </ul>
                    </li>
                    <li><a href="#_2_sparksql_初体验">2. SparkSQL 初体验</a>
                        <ul class="sectlevel2">
                            <li><a href="#_2_3_rdd_版本的_wordcount">2.3. RDD 版本的 WordCount</a></li>
                            <li><a href="#_2_2_命令式_api_的入门案例">2.2. 命令式 API 的入门案例</a></li>
                            <li><a href="#_2_2_sql_版本_wordcount">2.2. SQL 版本 WordCount</a></li>
                        </ul>
                    </li>
                    <li><a href="#_3_扩展_catalyst_优化器">3. [扩展] Catalyst 优化器</a>
                        <ul class="sectlevel2">
                            <li><a href="#_3_1_rdd_和_sparksql_运行时的区别">3.1. RDD 和 SparkSQL 运行时的区别</a></li>
                            <li><a href="#_3_2_catalyst">3.2. Catalyst</a></li>
                        </ul>
                    </li>
                    <li><a href="#_4_dataset_的特点">4. Dataset 的特点</a></li>
                    <li><a href="#_5_dataframe_的作用和常见操作">5. DataFrame 的作用和常见操作</a></li>
                    <li><a href="#_6_dataset_和_dataframe_的异同">6. Dataset 和 DataFrame 的异同</a></li>
                    <li><a href="#_7_数据读写">7. 数据读写</a>
                        <ul class="sectlevel2">
                            <li><a href="#_7_1_初识_dataframereader">7.1. 初识 DataFrameReader</a></li>
                            <li><a href="#_7_2_初识_dataframewriter">7.2. 初识 DataFrameWriter</a></li>
                            <li><a href="#_7_3_读写_parquet_格式文件">7.3. 读写 Parquet 格式文件</a></li>
                            <li><a href="#_7_4_读写_json_格式文件">7.4. 读写 JSON 格式文件</a></li>
                            <li><a href="#_7_5_访问_hive">7.5. 访问 Hive</a></li>
                            <li><a href="#_7_6_jdbc">7.6. JDBC</a></li>
                        </ul>
                    </li>
                    <li><a href="#_8_dataset_dataframe_的基础操作">8. Dataset (DataFrame) 的基础操作</a>
                        <ul class="sectlevel2">
                            <li><a href="#_8_1_有类型操作">8.1. 有类型操作</a></li>
                            <li><a href="#_8_2_无类型转换">8.2. 无类型转换</a></li>
                            <li><a href="#_8_5_column_对象">8.5. Column 对象</a></li>
                        </ul>
                    </li>
                    <li><a href="#_9_缺失值处理">9. 缺失值处理</a></li>
                    <li><a href="#_10_聚合">10. 聚合</a></li>
                    <li><a href="#_11_连接">11. 连接</a></li>
                </ul>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_1_sparksql_是什么">1. SparkSQL 是什么</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">目标</div>
                    <div class="content">
                        <div class="paragraph">
                            <p>对于一件事的理解, 应该分为两个大部分, 第一, 它是什么, 第二, 它解决了什么问题</p>
                        </div>
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>理解为什么会有 <code>SparkSQL</code></p>
                                </li>
                                <li>
                                    <p>理解 <code>SparkSQL</code> 所解决的问题, 以及它的使命</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_1_1_sparksql_的出现契机">1.1. SparkSQL 的出现契机</h3>
                    <div class="exampleblock">
                        <div class="title">目标</div>
                        <div class="content">
                            <div class="paragraph">
                                <p>理解 <code>SparkSQL</code> 是什么</p>
                            </div>
                        </div>
                    </div>
                    <div class="exampleblock">
                        <div class="title">主线</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p>历史前提</p>
                                    </li>
                                    <li>
                                        <p>发展过程</p>
                                    </li>
                                    <li>
                                        <p>重要性</p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                    <div class="sidebarblock">
                        <div class="content">
                            <div class="title">数据分析的方式</div>
                            <div class="paragraph">
                                <p>数据分析的方式大致上可以划分为 <code>SQL</code> 和 命令式两种</p>
                            </div>
                            <div class="dlist">
                                <dl>
                                    <dt class="hdlist1">命令式</dt>
                                    <dd>
                                        <div class="openblock">
                                            <div class="content">
                                                <div class="paragraph">
                                                    <p>在前面的 <code>RDD</code> 部分, 非常明显可以感觉的到是命令式的, 主要特征是通过一个算子, 可以得到一个结果,
                                                        通过结果再进行后续计算.</p>
                                                </div>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">sc.textFile("...")
  .flatMap(_.split(" "))
  .map((_, 1))
  .reduceByKey(_ + _)
  .collect()</code></pre>
                                                    </div>
                                                </div>
                                                <div class="dlist">
                                                    <dl>
                                                        <dt class="hdlist1">命令式的优点</dt>
                                                        <dd>
                                                            <div class="ulist">
                                                                <ul>
                                                                    <li>
                                                                        <p>操作粒度更细, 能够控制数据的每一个处理环节</p>
                                                                    </li>
                                                                    <li>
                                                                        <p>操作更明确, 步骤更清晰, 容易维护</p>
                                                                    </li>
                                                                    <li>
                                                                        <p>支持非结构化数据的操作</p>
                                                                    </li>
                                                                </ul>
                                                            </div>
                                                        </dd>
                                                        <dt class="hdlist1">命令式的缺点</dt>
                                                        <dd>
                                                            <div class="ulist">
                                                                <ul>
                                                                    <li>
                                                                        <p>需要一定的代码功底</p>
                                                                    </li>
                                                                    <li>
                                                                        <p>写起来比较麻烦</p>
                                                                    </li>
                                                                </ul>
                                                            </div>
                                                        </dd>
                                                    </dl>
                                                </div>
                                            </div>
                                        </div>
                                    </dd>
                                    <dt class="hdlist1">SQL</dt>
                                    <dd>
                                        <div class="openblock">
                                            <div class="content">
                                                <div class="paragraph">
                                                    <p>对于一些数据科学家, 要求他们为了做一个非常简单的查询, 写一大堆代码, 明显是一件非常残忍的事情, 所以 <code>SQL
                                                        on Hadoop</code> 是一个非常重要的方向.</p>
                                                </div>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-sql hljs" data-lang="sql">SELECT
	name,
	age,
	school
FROM students
WHERE age &gt; 10</code></pre>
                                                    </div>
                                                </div>
                                                <div class="dlist">
                                                    <dl>
                                                        <dt class="hdlist1">SQL 的优点</dt>
                                                        <dd>
                                                            <div class="ulist">
                                                                <ul>
                                                                    <li>
                                                                        <p>表达非常清晰, 比如说这段 <code>SQL</code> 明显就是为了查询三个字段,
                                                                            又比如说这段 <code>SQL</code> 明显能看到是想查询年龄大于 10
                                                                            岁的条目</p>
                                                                    </li>
                                                                </ul>
                                                            </div>
                                                        </dd>
                                                        <dt class="hdlist1">SQL 的缺点</dt>
                                                        <dd>
                                                            <div class="ulist">
                                                                <ul>
                                                                    <li>
                                                                        <p>想想一下 3 层嵌套的 <code>SQL</code>, 维护起来应该挺力不从心的吧
                                                                        </p>
                                                                    </li>
                                                                    <li>
                                                                        <p>试想一下, 如果使用 <code>SQL</code> 来实现机器学习算法, 也挺为难的吧
                                                                        </p>
                                                                    </li>
                                                                </ul>
                                                            </div>
                                                        </dd>
                                                    </dl>
                                                </div>
                                            </div>
                                        </div>
                                    </dd>
                                </dl>
                            </div>
                            <div class="paragraph">
                                <p><code>SQL</code> 擅长数据分析和通过简单的语法表示查询, 命令式操作适合过程式处理和算法性的处理. 在 <code>Spark</code> 出现之前,
                                    对于结构化数据的查询和处理, 一个工具一向只能支持 <code>SQL</code> 或者命令式, 使用者被迫要使用多个工具来适应两种场景,
                                    并且多个工具配合起来比较费劲.</p>
                            </div>
                            <div class="paragraph">
                                <p>而 <code>Spark</code> 出现了以后, 统一了两种数据处理范式, 是一种革新性的进步.</p>
                            </div>
                        </div>
                    </div>
                    <div class="paragraph">
                        <p>因为 <code>SQL</code> 是数据分析领域一个非常重要的范式, 所以 <code>Spark</code> 一直想要支持这种范式, 而伴随着一些决策失误,
                            这个过程其实还是非常曲折的</p>
                    </div>
                    <div class="paragraph">
                        <p><span class="image"><img
                                alt="7a1cdf107b8636713c2502a99d058061"
                                src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/7a1cdf107b8636713c2502a99d058061.png"></span></p>
                    </div>
                    <div class="dlist">
                        <dl>
                            <dt class="hdlist1">Hive</dt>
                            <dd>
                                <div class="exampleblock">
                                    <div class="content">
                                        <div class="dlist">
                                            <dl>
                                                <dt class="hdlist1">解决的问题</dt>
                                                <dd>
                                                    <div class="ulist">
                                                        <ul>
                                                            <li>
                                                                <p><code>Hive</code> 实现了 <code>SQL on Hadoop</code>, 使用
                                                                    <code>MapReduce</code> 执行任务</p>
                                                            </li>
                                                            <li>
                                                                <p>简化了 <code>MapReduce</code> 任务</p>
                                                            </li>
                                                        </ul>
                                                    </div>
                                                </dd>
                                                <dt class="hdlist1">新的问题</dt>
                                                <dd>
                                                    <div class="ulist">
                                                        <ul>
                                                            <li>
                                                                <p><code>Hive</code> 的查询延迟比较高, 原因是<strong>使用 <code>MapReduce</code>
                                                                    做调度</strong></p>
                                                            </li>
                                                        </ul>
                                                    </div>
                                                </dd>
                                            </dl>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">Shark</dt>
                            <dd>
                                <div class="exampleblock">
                                    <div class="content">
                                        <div class="dlist">
                                            <dl>
                                                <dt class="hdlist1">解决的问题</dt>
                                                <dd>
                                                    <div class="ulist">
                                                        <ul>
                                                            <li>
                                                                <p><code>Shark</code> 改写 <code>Hive</code> 的物理执行计划,
                                                                    使<strong>用 <code>Spark</code> 作业代替
                                                                        <code>MapReduce</code> 执行物理计划</strong></p>
                                                            </li>
                                                            <li>
                                                                <p>使用列式内存存储</p>
                                                            </li>
                                                            <li>
                                                                <p>以上两点使得 <code>Shark</code> 的查询效率很高</p>
                                                            </li>
                                                        </ul>
                                                    </div>
                                                </dd>
                                                <dt class="hdlist1">新的问题</dt>
                                                <dd>
                                                    <div class="ulist">
                                                        <ul>
                                                            <li>
                                                                <p><code>Shark</code> 重用了 <code>Hive</code> 的
                                                                    <code>SQL</code> 解析, 逻辑计划生成以及优化, 所以其实可以认为 <code>Shark</code>
                                                                    只是把 <code>Hive</code> 的物理执行替换为了 <code>Spark</code>
                                                                    作业</p>
                                                            </li>
                                                            <li>
                                                                <p>执行计划的生成严重依赖 <code>Hive</code>, 想要增加新的优化非常困难</p>
                                                            </li>
                                                            <li>
                                                                <p><code>Hive</code> 使用 <code>MapReduce</code> 执行作业,
                                                                    <strong>所以 <code>Hive</code> 是进程级别的并行</strong>, 而
                                                                    <code>Spark</code> 是线程级别的并行, 所以 <code>Hive</code>
                                                                    中很多线程不安全的代码不适用于 <code>Spark</code></p>
                                                            </li>
                                                        </ul>
                                                    </div>
                                                </dd>
                                            </dl>
                                        </div>
                                        <div class="paragraph">
                                            <p>由于以上问题, <code>Shark</code> 维护了 <code>Hive</code> 的一个分支, 并且无法合并进主线, 难以为继
                                            </p>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1"><code>SparkSQL</code></dt>
                            <dd>
                                <div class="exampleblock">
                                    <div class="content">
                                        <div class="dlist">
                                            <dl>
                                                <dt class="hdlist1">解决的问题</dt>
                                                <dd>
                                                    <div class="ulist">
                                                        <ul>
                                                            <li>
                                                                <p><code>Spark SQL</code> 使用 <code>Hive</code> 解析 <code>SQL</code>
                                                                    生成 <code>AST</code> 语法树, 将其后的逻辑计划生成, 优化, 物理计划都自己完成,
                                                                    而不依赖 <code>Hive</code></p>
                                                            </li>
                                                            <li>
                                                                <p>执行计划和优化交给优化器 <code>Catalyst</code></p>
                                                            </li>
                                                            <li>
                                                                <p>内建了一套简单的 <code>SQL</code> 解析器, 可以不使用 <code>HQL</code>,
                                                                    此外, 还引入和 <code>DataFrame</code> 这样的 <code>DSL
                                                                        API</code>, 完全可以不依赖任何 <code>Hive</code> 的组件</p>
                                                            </li>
                                                            <li>
                                                                <p><code>Shark</code> 只能查询文件, <code>Spark SQL</code>
                                                                    可以直接降查询作用于 <code>RDD</code>, 这一点是一个大进步</p>
                                                            </li>
                                                        </ul>
                                                    </div>
                                                </dd>
                                                <dt class="hdlist1">新的问题</dt>
                                                <dd>
                                                    <p>对于初期版本的 <code>SparkSQL</code>, 依然有挺多问题, 例如只能支持 <code>SQL</code>
                                                        的使用, 不能很好的兼容命令式, 入口不够统一等</p>
                                                </dd>
                                            </dl>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1"><code>Dataset</code></dt>
                            <dd>
                                <div class="exampleblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p><code>SparkSQL</code> 在 2.0 时代, 增加了一个新的 <code>API</code>, 叫做 <code>Dataset</code>,
                                                <code>Dataset</code> 统一和结合了 <code>SQL</code> 的访问和命令式 <code>API</code>
                                                的使用, 这是一个划时代的进步</p>
                                        </div>
                                        <div class="paragraph">
                                            <p>在 <code>Dataset</code> 中可以轻易的做到使用 <code>SQL</code> 查询并且筛选数据, 然后使用命令式
                                                <code>API</code> 进行探索式分析</p>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                        </dl>
                    </div>
                    <div class="admonitionblock note">
                        <table>
                            <tr>
                                <td class="icon">
                                    <i class="fa icon-note" title="Note"></i>
                                </td>
                                <td class="content">
                                    <div class="title">重要性</div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="9b1db9d54c796e0eb6769cafd2ef19ac"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/9b1db9d54c796e0eb6769cafd2ef19ac.png">
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p><code>SparkSQL</code> 不只是一个 <code>SQL</code> 引擎, <code>SparkSQL</code>
                                            也包含了一套对 <strong>结构化数据的命令式 <code>API</code></strong>, 事实上, 所有
                                            <code>Spark</code> 中常见的工具, 都是依赖和依照于 <code>SparkSQL</code> 的 <code>API</code>
                                            设计的</p>
                                    </div>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="exampleblock">
                        <div class="title">总结: <code>SparkSQL</code> 是什么</div>
                        <div class="content">
                            <div class="paragraph">
                                <p><code>SparkSQL</code> 是一个为了支持 <code>SQL</code> 而设计的工具, 但同时也支持命令式的 <code>API</code>
                                </p>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_1_2_sparksql_的适用场景">1.2. SparkSQL 的适用场景</h3>
                    <div class="exampleblock">
                        <div class="title">目标</div>
                        <div class="content">
                            <div class="paragraph">
                                <p>理解 <code>SparkSQL</code> 的适用场景</p>
                            </div>
                        </div>
                    </div>
                    <table class="tableblock frame-all grid-all stretch">
                        <colgroup>
                            <col style="width: 25%;">
                            <col style="width: 25%;">
                            <col style="width: 25%;">
                            <col style="width: 25%;">
                        </colgroup>
                        <thead>
                        <tr>
                            <th class="tableblock halign-left valign-top"></th>
                            <th class="tableblock halign-left valign-top">定义</th>
                            <th class="tableblock halign-left valign-top">特点</th>
                            <th class="tableblock halign-left valign-top">举例</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>结构化数据</strong>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">有固定的 <code>Schema</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">有预定义的
                                <code>Schema</code></p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">关系型数据库的表</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>半结构化数据</strong>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">没有固定的
                                <code>Schema</code>, 但是有结构</p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">没有固定的
                                <code>Schema</code>, 有结构信息, 数据一般是自描述的</p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">指一些有结构的文件格式, 例如 <code>JSON</code>
                            </p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><strong>非结构化数据</strong>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">没有固定 <code>Schema</code>,
                                也没有结构</p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">没有固定 <code>Schema</code>,
                                也没有结构</p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">指文档图片之类的格式</p></td>
                        </tr>
                        </tbody>
                    </table>
                    <div class="dlist">
                        <dl>
                            <dt class="hdlist1">结构化数据</dt>
                            <dd>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>一般指数据有固定的 <code>Schema</code>, 例如在用户表中, <code>name</code> 字段是 <code>String</code>
                                                型, 那么每一条数据的 <code>name</code> 字段值都可以当作 <code>String</code> 来使用</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code>+----+--------------+---------------------------+-------+---------+
| id | name         | url                       | alexa | country |
+----+--------------+---------------------------+-------+---------+
| 1  | Google       | https://www.google.cm/    | 1     | USA     |
| 2  | 淘宝          | https://www.taobao.com/   | 13    | CN      |
| 3  | 菜鸟教程      | http://www.runoob.com/    | 4689  | CN      |
| 4  | 微博          | http://weibo.com/         | 20    | CN      |
| 5  | Facebook     | https://www.facebook.com/ | 3     | USA     |
+----+--------------+---------------------------+-------+---------+</code></pre>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">半结构化数据</dt>
                            <dd>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>一般指的是数据没有固定的 <code>Schema</code>, 但是数据本身是有结构的</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
     "firstName": "John",
     "lastName": "Smith",
     "age": 25,
     "phoneNumber":
     [
         {
           "type": "home",
           "number": "212 555-1234"
         },
         {
           "type": "fax",
           "number": "646 555-4567"
         }
     ]
 }</code></pre>
                                            </div>
                                        </div>
                                        <div class="dlist">
                                            <dl>
                                                <dt class="hdlist1">没有固定 <code>Schema</code></dt>
                                                <dd>
                                                    <p>指的是半结构化数据是没有固定的 <code>Schema</code> 的, 可以理解为没有显式指定
                                                        <code>Schema</code><br>
                                                        比如说一个用户信息的 <code>JSON</code> 文件, 第一条数据的 <code>phone_num</code>
                                                        有可能是 <code>String</code>, 第二条数据虽说应该也是 <code>String</code>,
                                                        但是如果硬要指定为 <code>BigInt</code>, 也是有可能的<br>
                                                        因为没有指定 <code>Schema</code>, 没有显式的强制的约束</p>
                                                </dd>
                                                <dt class="hdlist1">有结构</dt>
                                                <dd>
                                                    <p>虽说半结构化数据是没有显式指定 <code>Schema</code> 的, 也没有约束,
                                                        但是半结构化数据本身是有有隐式的结构的, 也就是数据自身可以描述自身<br>
                                                        例如 <code>JSON</code> 文件, 其中的某一条数据是有字段这个概念的, 每个字段也有类型的概念, 所以说
                                                        <code>JSON</code> 是可以描述自身的, 也就是数据本身携带有元信息</p>
                                                </dd>
                                            </dl>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1"><code>SparkSQL</code> 处理什么数据的问题?</dt>
                            <dd>
                                <div class="ulist">
                                    <ul>
                                        <li>
                                            <p><code>Spark</code> 的 <code>RDD</code> 主要用于处理 <strong>非结构化数据</strong> 和
                                                <strong>半结构化数据</strong></p>
                                        </li>
                                        <li>
                                            <p><code>SparkSQL</code> 主要用于处理 <strong>结构化数据</strong></p>
                                        </li>
                                    </ul>
                                </div>
                            </dd>
                            <dt class="hdlist1"><code>SparkSQL</code> 相较于 <code>RDD</code> 的优势在哪?</dt>
                            <dd>
                                <div class="ulist">
                                    <ul>
                                        <li>
                                            <p><code>SparkSQL</code> 提供了更好的外部数据源读写支持</p>
                                            <div class="ulist">
                                                <ul>
                                                    <li>
                                                        <p>因为大部分外部数据源是有结构化的, 需要在 <code>RDD</code> 之外有一个新的解决方案,
                                                            来整合这些结构化数据源</p>
                                                    </li>
                                                </ul>
                                            </div>
                                        </li>
                                        <li>
                                            <p><code>SparkSQL</code> 提供了直接访问列的能力</p>
                                            <div class="ulist">
                                                <ul>
                                                    <li>
                                                        <p>因为 <code>SparkSQL</code> 主要用做于处理结构化数据, 所以其提供的
                                                            <code>API</code> 具有一些普通数据库的能力</p>
                                                    </li>
                                                </ul>
                                            </div>
                                        </li>
                                    </ul>
                                </div>
                            </dd>
                        </dl>
                    </div>
                    <div class="exampleblock">
                        <div class="title">总结: <code>SparkSQL</code> 适用于什么场景?</div>
                        <div class="content">
                            <div class="paragraph">
                                <p><code>SparkSQL</code> 适用于处理结构化数据的场景</p>
                            </div>
                        </div>
                    </div>
                    <div class="exampleblock">
                        <div class="title">本章总结</div>
                        <div class="content">
                            <div class="ulist">
                                <ul>
                                    <li>
                                        <p><code>SparkSQL</code> 是一个即支持 <code>SQL</code> 又支持命令式数据处理的工具</p>
                                    </li>
                                    <li>
                                        <p><code>SparkSQL</code> 的主要适用场景是处理结构化数据</p>
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_2_sparksql_初体验">2. SparkSQL 初体验</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">目标</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>了解 <code>SparkSQL</code> 的 <code>API</code> 由哪些部分组成</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_2_3_rdd_版本的_wordcount">2.3. RDD 版本的 WordCount</h3>
                    <div class="listingblock">
                        <div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">val config = new SparkConf().setAppName("ip_ana").setMaster("local[6]")
val sc = new SparkContext(config)

sc.textFile("hdfs://node01:8020/dataset/wordcount.txt")
  .flatMap(_.split(" "))
  .map((_, 1))
  .reduceByKey(_ + _)
  .collect</code></pre>
                        </div>
                    </div>
                    <div class="ulist">
                        <ul>
                            <li>
                                <p><code>RDD</code> 版本的代码有一个非常明显的特点, 就是它所处理的数据是基本类型的, 在算子中对整个数据进行处理</p>
                            </li>
                        </ul>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_2_2_命令式_api_的入门案例">2.2. 命令式 API 的入门案例</h3>
                    <div class="listingblock">
                        <div class="content">
<pre class="highlightjs highlight"><code>case class People(name: String, age: Int)

val spark: SparkSession = new sql.SparkSession.Builder()       <i class="conum" data-value="1"></i><b>(1)</b>
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val peopleRDD: RDD[People] = spark.sparkContext.parallelize(Seq(People("zhangsan", 9), People("lisi", 15)))
val peopleDS: Dataset[People] = peopleRDD.toDS()               <i class="conum" data-value="2"></i><b>(2)</b>
val teenagers: Dataset[String] = peopleDS.where('age &gt; 10)     <i class="conum" data-value="3"></i><b>(3)</b>
  .where('age &lt; 20)
  .select('name)
  .as[String]

/*
+----+
|name|
+----+
|lisi|
+----+
*/
teenagers.show()</code></pre>
                        </div>
                    </div>
                    <div class="colist arabic">
                        <table>
                            <tr>
                                <td><i class="conum" data-value="1"></i><b>1</b></td>
                                <td>SparkSQL 中有一个新的入口点, 叫做 SparkSession</td>
                            </tr>
                            <tr>
                                <td><i class="conum" data-value="2"></i><b>2</b></td>
                                <td>SparkSQL 中有一个新的类型叫做 Dataset</td>
                            </tr>
                            <tr>
                                <td><i class="conum" data-value="3"></i><b>3</b></td>
                                <td>SparkSQL 有能力直接通过字段名访问数据集, 说明 SparkSQL 的 API 中是携带 Schema 信息的</td>
                            </tr>
                        </table>
                    </div>
                    <div class="sidebarblock">
                        <div class="content">
                            <div class="title">SparkSession</div>
                            <div class="dlist">
                                <dl>
                                    <dt class="hdlist1"><code>SparkContext</code> 作为 <code>RDD</code> 的创建者和入口,
                                        其主要作用有如下两点
                                    </dt>
                                    <dd>
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p>创建 <code>RDD</code>, 主要是通过读取文件创建 <code>RDD</code></p>
                                                </li>
                                                <li>
                                                    <p>监控和调度任务, 包含了一系列组件, 例如 <code>DAGScheduler</code>, <code>TaskSheduler</code>
                                                    </p>
                                                </li>
                                            </ul>
                                        </div>
                                    </dd>
                                    <dt class="hdlist1">为什么无法使用 <code>SparkContext</code> 作为 <code>SparkSQL</code> 的入口?
                                    </dt>
                                    <dd>
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p><code>SparkContext</code> 在读取文件的时候, 是不包含 <code>Schema</code> 信息的,
                                                        因为读取出来的是 <code>RDD</code></p>
                                                </li>
                                                <li>
                                                    <p><code>SparkContext</code> 在整合数据源如 <code>Cassandra</code>, <code>JSON</code>,
                                                        <code>Parquet</code> 等的时候是不灵活的, 而 <code>DataFrame</code> 和
                                                        <code>Dataset</code> 一开始的设计目标就是要支持更多的数据源</p>
                                                </li>
                                                <li>
                                                    <p><code>SparkContext</code> 的调度方式是直接调度 <code>RDD</code>,
                                                        但是一般情况下针对结构化数据的访问, 会先通过优化器优化一下</p>
                                                </li>
                                            </ul>
                                        </div>
                                    </dd>
                                </dl>
                            </div>
                            <div class="paragraph">
                                <p>所以 <code>SparkContext</code> 确实已经不适合作为 <code>SparkSQL</code> 的入口, 所以刚开始的时候 <code>Spark</code>
                                    团队为 <code>SparkSQL</code> 设计了两个入口点, 一个是 <code>SQLContext</code> 对应
                                    <code>Spark</code> 标准的 <code>SQL</code> 执行, 另外一个是 <code>HiveContext</code> 对应 <code>HiveSQL</code>
                                    的执行和 <code>Hive</code> 的支持.</p>
                            </div>
                            <div class="paragraph">
                                <p>在 <code>Spark 2.0</code> 的时候, 为了解决入口点不统一的问题, 创建了一个新的入口点 <code>SparkSession</code>,
                                    作为整个 <code>Spark</code> 生态工具的统一入口点, 包括了 <code>SQLContext</code>,
                                    <code>HiveContext</code>, <code>SparkContext</code> 等组件的功能</p>
                            </div>
                            <div class="dlist">
                                <dl>
                                    <dt class="hdlist1">新的入口应该有什么特性?</dt>
                                    <dd>
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p>能够整合 <code>SQLContext</code>, <code>HiveContext</code>, <code>SparkContext</code>,
                                                        <code>StreamingContext</code> 等不同的入口点</p>
                                                </li>
                                                <li>
                                                    <p>为了支持更多的数据源, 应该完善读取和写入体系</p>
                                                </li>
                                                <li>
                                                    <p>同时对于原来的入口点也不能放弃, 要向下兼容</p>
                                                </li>
                                            </ul>
                                        </div>
                                    </dd>
                                </dl>
                            </div>
                        </div>
                    </div>
                    <div class="sidebarblock">
                        <div class="content">
                            <div class="title">DataFrame &amp; Dataset</div>
                            <div class="imageblock">
                                <div class="content">
                                    <img alt="eca0d2e1e2b5ce678161438d87707b61"
                                         src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/eca0d2e1e2b5ce678161438d87707b61.png">
                                </div>
                            </div>
                            <div class="paragraph">
                                <p><code>SparkSQL</code> 最大的特点就是它针对于结构化数据设计, 所以 <code>SparkSQL</code> 应该是能支持针对某一个字段的访问的,
                                    而这种访问方式有一个前提, 就是 <code>SparkSQL</code> 的数据集中, 要 <strong>包含结构化信息</strong>, 也就是俗称的
                                    <code>Schema</code></p>
                            </div>
                            <div class="paragraph">
                                <p>而 <code>SparkSQL</code> 对外提供的 <code>API</code> 有两类, 一类是直接执行 <code>SQL</code>,
                                    另外一类就是命令式. <code>SparkSQL</code> 提供的命令式 <code>API</code> 就是 <code>DataFrame</code> 和
                                    <code>Dataset</code>, 暂时也可以认为 <code>DataFrame</code> 就是 <code>Dataset</code>, 只是在不同的
                                    <code>API</code> 中返回的是 <code>Dataset</code> 的不同表现形式</p>
                            </div>
                            <div class="listingblock">
                                <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">// RDD
rdd.map { case Person(id, name, age) =&gt; (age, 1) }
  .reduceByKey {case ((age, count), (totalAge, totalCount)) =&gt; (age, count + totalCount)}

// DataFrame
df.groupBy("age").count("age")</code></pre>
                                </div>
                            </div>
                            <div class="paragraph">
                                <p>通过上面的代码, 可以清晰的看到, <code>SparkSQL</code> 的命令式操作相比于 <code>RDD</code> 来说, 可以直接通过 <code>Schema</code>
                                    信息来访问其中某个字段, 非常的方便</p>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_2_2_sql_版本_wordcount">2.2. SQL 版本 WordCount</h3>
                    <div class="listingblock">
                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val peopleRDD: RDD[People] = spark.sparkContext.parallelize(Seq(People("zhangsan", 9), People("lisi", 15)))
val peopleDS: Dataset[People] = peopleRDD.toDS()
peopleDS.createOrReplaceTempView("people")

val teenagers: DataFrame = spark.sql("select name from people where age &gt; 10 and age &lt; 20")

/*
+----+
|name|
+----+
|lisi|
+----+
 */
teenagers.show()</code></pre>
                        </div>
                    </div>
                    <div class="paragraph">
                        <p>以往使用 <code>SQL</code> 肯定是要有一个表的, 在 <code>Spark</code> 中, 并不存在表的概念, 但是有一个近似的概念, 叫做 <code>DataFrame</code>,
                            所以一般情况下要先通过 <code>DataFrame</code> 或者 <code>Dataset</code> 注册一张临时表, 然后使用 <code>SQL</code>
                            操作这张临时表</p>
                    </div>
                    <div class="exampleblock">
                        <div class="title">总结</div>
                        <div class="content">
                            <div class="paragraph">
                                <p><code>SparkSQL</code> 提供了 <code>SQL</code> 和 命令式 <code>API</code> 两种不同的访问结构化数据的形式,
                                    并且它们之间可以无缝的衔接</p>
                            </div>
                            <div class="paragraph">
                                <p>命令式 <code>API</code> 由一个叫做 <code>Dataset</code> 的组件提供, 其还有一个变形, 叫做
                                    <code>DataFrame</code></p>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_3_扩展_catalyst_优化器">3. [扩展] Catalyst 优化器</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">目标</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>理解 <code>SparkSQL</code> 和以 <code>RDD</code> 为代表的 <code>SparkCore</code> 最大的区别
                                    </p>
                                </li>
                                <li>
                                    <p>理解优化器的运行原理和作用</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_3_1_rdd_和_sparksql_运行时的区别">3.1. RDD 和 SparkSQL 运行时的区别</h3>
                    <div class="dlist">
                        <dl>
                            <dt class="hdlist1"><code>RDD</code> 的运行流程</dt>
                            <dd>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="1e627dcc1dc31f721933d3e925fa318b"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/1e627dcc1dc31f721933d3e925fa318b.png">
                                            </div>
                                        </div>
                                        <div class="dlist">
                                            <dl>
                                                <dt class="hdlist1">大致运行步骤</dt>
                                                <dd>
                                                    <p>先将 <code>RDD</code> 解析为由 <code>Stage</code> 组成的 <code>DAG</code>,
                                                        后将 <code>Stage</code> 转为 <code>Task</code> 直接运行</p>
                                                </dd>
                                                <dt class="hdlist1">问题</dt>
                                                <dd>
                                                    <p>任务会按照代码所示运行, 依赖开发者的优化, 开发者的会在很大程度上影响运行效率</p>
                                                </dd>
                                                <dt class="hdlist1">解决办法</dt>
                                                <dd>
                                                    <p>创建一个组件, 帮助开发者修改和优化代码, 但是这在 <code>RDD</code> 上是无法实现的</p>
                                                </dd>
                                            </dl>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">为什么 <code>RDD</code> 无法自我优化?</dt>
                            <dd>
                                <div class="ulist">
                                    <ul>
                                        <li>
                                            <p><code>RDD</code> 没有 <code>Schema</code> 信息</p>
                                        </li>
                                        <li>
                                            <p><code>RDD</code> 可以同时处理结构化和非结构化的数据</p>
                                        </li>
                                    </ul>
                                </div>
                            </dd>
                            <dt class="hdlist1"><code>SparkSQL</code> 提供了什么?</dt>
                            <dd>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="72e4d163c029f86fafcfa083e6cf6eda"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/72e4d163c029f86fafcfa083e6cf6eda.png">
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p>和 <code>RDD</code> 不同, <code>SparkSQL</code> 的 <code>Dataset</code> 和
                                                <code>SQL</code> 并不是直接生成计划交给集群执行, 而是经过了一个叫做 <code>Catalyst</code> 的优化器,
                                                这个优化器能够自动帮助开发者优化代码</p>
                                        </div>
                                        <div class="paragraph">
                                            <p>也就是说, 在 <code>SparkSQL</code> 中, 开发者的代码即使不够优化, 也会被优化为相对较好的形式去执行</p>
                                        </div>
                                        <div class="dlist">
                                            <dl>
                                                <dt class="hdlist1">为什么 <code>SparkSQL</code> 提供了这种能力?</dt>
                                                <dd>
                                                    <p>首先, <code>SparkSQL</code> 大部分情况用于处理结构化数据和半结构化数据, 所以 <code>SparkSQL</code>
                                                        可以获知数据的 <code>Schema</code>, 从而根据其 <code>Schema</code> 来进行优化</p>
                                                </dd>
                                            </dl>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                        </dl>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_3_2_catalyst">3.2. Catalyst</h3>
                    <div class="admonitionblock note">
                        <table>
                            <tr>
                                <td class="icon">
                                    <i class="fa icon-note" title="Note"></i>
                                </td>
                                <td class="content">
                                    <div class="paragraph">
                                        <p>为了解决过多依赖 <code>Hive</code> 的问题, <code>SparkSQL</code> 使用了一个新的
                                            <code>SQL</code> 优化器替代 <code>Hive</code> 中的优化器, 这个优化器就是
                                            <code>Catalyst</code>, 整个 <code>SparkSQL</code> 的架构大致如下</p>
                                    </div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="4d025ea8579395f704702eb94572b8de"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/4d025ea8579395f704702eb94572b8de.png">
                                        </div>
                                    </div>
                                    <div class="olist arabic">
                                        <ol class="arabic">
                                            <li>
                                                <p><code>API</code> 层简单的说就是 <code>Spark</code> 会通过一些 <code>API</code> 接受
                                                    <code>SQL</code> 语句</p>
                                            </li>
                                            <li>
                                                <p>收到 <code>SQL</code> 语句以后, 将其交给 <code>Catalyst</code>,
                                                    <code>Catalyst</code> 负责解析 <code>SQL</code>, 生成执行计划等</p>
                                            </li>
                                            <li>
                                                <p><code>Catalyst</code> 的输出应该是 <code>RDD</code> 的执行计划</p>
                                            </li>
                                            <li>
                                                <p>最终交由集群运行</p>
                                            </li>
                                        </ol>
                                    </div>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="imageblock">
                        <div class="content">
                            <img alt="67b14d92b21b191914800c384cbed439"
                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/67b14d92b21b191914800c384cbed439.png">
                        </div>
                    </div>
                    <div class="dlist">
                        <dl>
                            <dt class="hdlist1">Step 1 : 解析 <code>SQL</code>, 并且生成 <code>AST</code> (抽象语法树)</dt>
                            <dd>
                                <div class="imageblock">
                                    <div class="content">
                                        <img alt="5c0e91faae9043400c11bf68c20031a2"
                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/5c0e91faae9043400c11bf68c20031a2.png">
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">Step 2 : 在 <code>AST</code> 中加入元数据信息, 做这一步主要是为了一些优化, 例如 <code>col =
                                col</code> 这样的条件, 下图是一个简略图, 便于理解
                            </dt>
                            <dd>
                                <div class="imageblock">
                                    <div class="content">
                                        <img alt="02afbb7533249cc6024c2dfc2ee4891e"
                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/02afbb7533249cc6024c2dfc2ee4891e.png">
                                    </div>
                                </div>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p><code>score.id &#8594; id#1#L</code> 为 <code>score.id</code> 生成
                                                        <code>id</code> 为 1, 类型是 <code>Long</code></p>
                                                </li>
                                                <li>
                                                    <p><code>score.math_score &#8594; math_score#2#L</code> 为 <code>score.math_score</code>
                                                        生成 <code>id</code> 为 2, 类型为 <code>Long</code></p>
                                                </li>
                                                <li>
                                                    <p><code>people.id &#8594; id#3#L</code> 为 <code>people.id</code> 生成
                                                        <code>id</code> 为 3, 类型为 <code>Long</code></p>
                                                </li>
                                                <li>
                                                    <p><code>people.age &#8594; age#4#L</code> 为 <code>people.age</code>
                                                        生成 <code>id</code> 为 4, 类型为 <code>Long</code></p>
                                                </li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">Step 3 : 对已经加入元数据的 <code>AST</code>, 输入优化器, 进行优化, 从两种常见的优化开始, 简单介绍</dt>
                            <dd>
                                <div class="imageblock">
                                    <div class="content">
                                        <img alt="07142425c65dc6d921451a8bdec8a29d"
                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/07142425c65dc6d921451a8bdec8a29d.png">
                                    </div>
                                </div>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p>谓词下推 <code>Predicate Pushdown</code>, 将 <code>Filter</code>
                                                        这种可以减小数据集的操作下推, 放在 <code>Scan</code> 的位置, 这样可以减少操作时候的数据量</p>
                                                </li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                                <div class="imageblock">
                                    <div class="content">
                                        <img alt="7b58443ef6ace60d269d704c1f4eae21"
                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/7b58443ef6ace60d269d704c1f4eae21.png">
                                    </div>
                                </div>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p>列值裁剪 <code>Column Pruning</code>, 在谓词下推后, <code>people</code>
                                                        表之上的操作只用到了 <code>id</code> 列, 所以可以把其它列裁剪掉, 这样可以减少处理的数据量,
                                                        从而优化处理速度</p>
                                                </li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p>还有其余很多优化点, 大概一共有一二百种, 随着 <code>SparkSQL</code> 的发展, 还会越来越多,
                                                        感兴趣的同学可以继续通过源码了解, 源码在 <code>org.apache.spark.sql.catalyst.optimizer.Optimizer</code>
                                                    </p>
                                                </li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">Step 4 : 上面的过程生成的 <code>AST</code> 其实最终还没办法直接运行, 这个 <code>AST</code> 叫做
                                <code>逻辑计划</code>, 结束后, 需要生成 <code>物理计划</code>, 从而生成 <code>RDD</code> 来运行
                            </dt>
                            <dd>
                                <div class="openblock">
                                    <div class="content">
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p>在生成`物理计划`的时候, 会经过`成本模型`对整棵树再次执行优化, 选择一个更好的计划</p>
                                                </li>
                                                <li>
                                                    <p>在生成`物理计划`以后, 因为考虑到性能, 所以会使用代码生成, 在机器中运行</p>
                                                </li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                        </dl>
                    </div>
                    <div class="admonitionblock note">
                        <table>
                            <tr>
                                <td class="icon">
                                    <i class="fa icon-note" title="Note"></i>
                                </td>
                                <td class="content">
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">可以使用 <code>queryExecution</code> 方法查看逻辑执行计划, 使用 <code>explain</code>
                                                方法查看物理执行计划
                                            </dt>
                                            <dd>
                                                <div class="imageblock">
                                                    <div class="content">
                                                        <img alt="6dd59b15 d810 4f1e ab52 c1ecfe0bddcd"
                                                             src="http://nos.netease.com/knowledge/6dd59b15-d810-4f1e-ab52-c1ecfe0bddcd" width="700">
                                                    </div>
                                                </div>
                                                <div class="imageblock">
                                                    <div class="content">
                                                        <img alt="6281b141 af94 41e7 8953 d33b0a6d04d0"
                                                             src="http://nos.netease.com/knowledge/6281b141-af94-41e7-8953-d33b0a6d04d0" width="700">
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">也可以使用 <code>Spark WebUI</code> 进行查看</dt>
                                            <dd>
                                                <div class="imageblock">
                                                    <div class="content">
                                                        <img alt="7884408908284ba4ebc57b0f1360bc03"
                                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/7884408908284ba4ebc57b0f1360bc03.png" width="700">
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="exampleblock">
                        <div class="title">总结</div>
                        <div class="content">
                            <div class="paragraph">
                                <p><code>SparkSQL</code> 和 <code>RDD</code> 不同的主要点是在于其所操作的数据是结构化的, 提供了对数据更强的感知和分析能力,
                                    能够对代码进行更深层的优化, 而这种能力是由一个叫做 <code>Catalyst</code> 的优化器所提供的</p>
                            </div>
                            <div class="paragraph">
                                <p><code>Catalyst</code> 的主要运作原理是分为三步, 先对 <code>SQL</code> 或者 <code>Dataset</code>
                                    的代码解析, 生成逻辑计划, 后对逻辑计划进行优化, 再生成物理计划, 最后生成代码到集群中以 <code>RDD</code> 的形式运行</p>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_4_dataset_的特点">4. Dataset 的特点</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">目标</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>理解 <code>Dataset</code> 是什么</p>
                                </li>
                                <li>
                                    <p>理解 <code>Dataset</code> 的特性</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="dlist">
                    <dl>
                        <dt class="hdlist1"><code>Dataset</code> 是什么?</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val dataset: Dataset[People] = spark.createDataset(Seq(People("zhangsan", 9), People("lisi", 15)))
// 方式1: 通过对象来处理
dataset.filter(item =&gt; item.age &gt; 10).show()
// 方式2: 通过字段来处理
dataset.filter('age &gt; 10).show()
// 方式3: 通过类似SQL的表达式来处理
dataset.filter("age &gt; 10").show()</code></pre>
                                        </div>
                                    </div>
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">问题1: <code>People</code> 是什么?</dt>
                                            <dd>
                                                <p><code>People</code> 是一个强类型的类</p>
                                            </dd>
                                            <dt class="hdlist1">问题2: 这个 <code>Dataset</code> 中是结构化的数据吗?</dt>
                                            <dd>
                                                <p>非常明显是的, 因为 <code>People</code> 对象中有结构信息, 例如字段名和字段类型</p>
                                            </dd>
                                            <dt class="hdlist1">问题3: 这个 <code>Dataset</code> 能够使用类似 <code>SQL</code>
                                                这样声明式结构化查询语句的形式来查询吗?
                                            </dt>
                                            <dd>
                                                <p>当然可以, 已经演示过了</p>
                                            </dd>
                                            <dt class="hdlist1">问题4: <code>Dataset</code> 是什么?</dt>
                                            <dd>
                                                <p><code>Dataset</code> 是一个强类型, 并且类型安全的数据容器, 并且提供了结构化查询 <code>API</code>
                                                    和类似 <code>RDD</code> 一样的命令式 <code>API</code></p>
                                            </dd>
                                        </dl>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">即使使用 <code>Dataset</code> 的命令式 <code>API</code>, 执行计划也依然会被优化</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>Dataset</code> 具有 <code>RDD</code> 的方便, 同时也具有 <code>DataFrame</code>
                                            的性能优势, 并且 <code>Dataset</code> 还是强类型的, 能做到类型安全.</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-text hljs" data-lang="text">scala&gt; spark.range(1).filter('id === 0).explain(true)

== Parsed Logical Plan ==
'Filter ('id = 0)
+- Range (0, 1, splits=8)

== Analyzed Logical Plan ==
id: bigint
Filter (id#51L = cast(0 as bigint))
+- Range (0, 1, splits=8)

== Optimized Logical Plan ==
Filter (id#51L = 0)
+- Range (0, 1, splits=8)

== Physical Plan ==
*Filter (id#51L = 0)
+- *Range (0, 1, splits=8)</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>Dataset</code> 的底层是什么?</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>Dataset</code> 最底层处理的是对象的序列化形式, 通过查看 <code>Dataset</code> 生成的物理执行计划,
                                            也就是最终所处理的 <code>RDD</code>, 就可以判定 <code>Dataset</code> 底层处理的是什么形式的数据</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val dataset: Dataset[People] = spark.createDataset(Seq(People("zhangsan", 9), People("lisi", 15)))
val internalRDD: RDD[InternalRow] = dataset.queryExecution.toRdd</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p><code>dataset.queryExecution.toRdd</code> 这个 <code>API</code> 可以看到 <code>Dataset</code>
                                            底层执行的 <code>RDD</code>, 这个 <code>RDD</code> 中的范型是 <code>InternalRow</code>,
                                            <code>InternalRow</code> 又称之为 <code>Catalyst Row</code>, 是
                                            <code>Dataset</code> 底层的数据结构, 也就是说, 无论 <code>Dataset</code> 的范型是什么, 无论是
                                            <code>Dataset[Person]</code> 还是其它的, 其最底层进行处理的数据结构都是 <code>InternalRow</code>
                                        </p>
                                    </div>
                                    <div class="paragraph">
                                        <p>所以, <code>Dataset</code> 的范型对象在执行之前, 需要通过 <code>Encoder</code> 转换为 <code>InternalRow</code>,
                                            在输入之前, 需要把 <code>InternalRow</code> 通过 <code>Decoder</code> 转换为范型对象</p>
                                    </div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="cc610157b92466cac52248a8bf72b76e"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/cc610157b92466cac52248a8bf72b76e.png">
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">可以获取 <code>Dataset</code> 对应的 <code>RDD</code> 表示</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>在 <code>Dataset</code> 中, 可以使用一个属性 <code>rdd</code> 来得到它的 <code>RDD</code>
                                            表示, 例如 <code>Dataset[T] &#8594; RDD[T]</code></p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val dataset: Dataset[People] = spark.createDataset(Seq(People("zhangsan", 9), People("lisi", 15)))

/*
(2) MapPartitionsRDD[3] at rdd at Testing.scala:159 []
 |  MapPartitionsRDD[2] at rdd at Testing.scala:159 []
 |  MapPartitionsRDD[1] at rdd at Testing.scala:159 []
 |  ParallelCollectionRDD[0] at rdd at Testing.scala:159 []
 */
<i class="conum" data-value="1"></i><b>(1)</b>
println(dataset.rdd.toDebugString) // 这段代码的执行计划为什么多了两个步骤?

/*
(2) MapPartitionsRDD[5] at toRdd at Testing.scala:160 []
 |  ParallelCollectionRDD[4] at toRdd at Testing.scala:160 []
 */
<i class="conum" data-value="2"></i><b>(2)</b>
println(dataset.queryExecution.toRdd.toDebugString)</code></pre>
                                        </div>
                                    </div>
                                    <div class="colist arabic">
                                        <table>
                                            <tr>
                                                <td><i class="conum" data-value="1"></i><b>1</b></td>
                                                <td>使用 <code>Dataset.rdd</code> 将 <code>Dataset</code> 转为
                                                    <code>RDD</code> 的形式
                                                </td>
                                            </tr>
                                            <tr>
                                                <td><i class="conum" data-value="2"></i><b>2</b></td>
                                                <td><code>Dataset</code> 的执行计划底层的 <code>RDD</code></td>
                                            </tr>
                                        </table>
                                    </div>
                                    <div class="paragraph">
                                        <p>可以看到 <code>(1)</code> 对比 <code>(2)</code> 对了两个步骤, 这两个步骤的本质就是将
                                            <code>Dataset</code> 底层的 <code>InternalRow</code> 转为 <code>RDD</code>
                                            中的对象形式, 这个操作还是会有点重的, 所以慎重使用 <code>rdd</code> 属性来转换 <code>Dataset</code> 为
                                            <code>RDD</code></p>
                                    </div>
                                </div>
                            </div>
                        </dd>
                    </dl>
                </div>
                <div class="exampleblock">
                    <div class="title">总结</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p><code>Dataset</code> 是一个新的 <code>Spark</code> 组件, 其底层还是 <code>RDD</code></p>
                                </li>
                                <li>
                                    <p><code>Dataset</code> 提供了访问对象中某个特定字段的能力, 不用像 <code>RDD</code> 一样每次都要针对整个对象做操作</p>
                                </li>
                                <li>
                                    <p><code>Dataset</code> 和 <code>RDD</code> 不同, 如果想把 <code>Dataset[T]</code> 转为
                                        <code>RDD[T]</code>, 则需要对 <code>Dataset</code> 底层的 <code>InternalRow</code> 做转换,
                                        是一个比价重量级的操作</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_5_dataframe_的作用和常见操作">5. DataFrame 的作用和常见操作</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">目标</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>理解 <code>DataFrame</code> 是什么</p>
                                </li>
                                <li>
                                    <p>理解 <code>DataFrame</code> 的常见操作</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="dlist">
                    <dl>
                        <dt class="hdlist1"><code>DataFrame</code> 是什么?</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>DataFrame</code> 是 <code>SparkSQL</code> 中一个表示关系型数据库中 <code>表</code>
                                            的函数式抽象, 其作用是让 <code>Spark</code> 处理大规模结构化数据的时候更加容易. 一般
                                            <code>DataFrame</code> 可以处理结构化的数据, 或者是半结构化的数据, 因为这两类数据中都可以获取到
                                            <code>Schema</code> 信息. 也就是说 <code>DataFrame</code> 中有 <code>Schema</code>
                                            信息, 可以像操作表一样操作 <code>DataFrame</code>.</p>
                                    </div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="eca0d2e1e2b5ce678161438d87707b61"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/eca0d2e1e2b5ce678161438d87707b61.png">
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p><code>DataFrame</code> 由两部分构成, 一是 <code>row</code> 的集合, 每个 <code>row</code>
                                            对象表示一个行, 二是描述 <code>DataFrame</code> 结构的 <code>Schema</code>.</p>
                                    </div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="238c241593cd5b0fd06d4d74294680e2"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/238c241593cd5b0fd06d4d74294680e2.png">
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p><code>DataFrame</code> 支持 <code>SQL</code> 中常见的操作, 例如: <code>select</code>,
                                            <code>filter</code>, <code>join</code>, <code>group</code>,
                                            <code>sort</code>, <code>join</code> 等</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val peopleDF: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()

/*
+---+-----+
|age|count|
+---+-----+
| 15|    2|
+---+-----+
 */
peopleDF.groupBy('age)
  .count()
  .show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">通过隐式转换创建 <code>DataFrame</code></dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>这种方式本质上是使用 <code>SparkSession</code> 中的隐式转换来进行的</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

// 必须要导入隐式转换
// 注意: spark 在此处不是包, 而是 SparkSession 对象
import spark.implicits._

val peopleDF: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()</code></pre>
                                        </div>
                                    </div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="841503b4240e7a8ecac62d92203e9943"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/841503b4240e7a8ecac62d92203e9943.png" width="600">
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>根据源码可以知道, <code>toDF</code> 方法可以在 <code>RDD</code> 和 <code>Seq</code> 中使用</p>
                                    </div>
                                    <div class="paragraph">
                                        <p>通过集合创建 <code>DataFrame</code> 的时候, 集合中不仅可以包含样例类, 也可以只有普通数据类型, 后通过指定列名来创建</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val df1: DataFrame = Seq("nihao", "hello").toDF("text")

/*
+-----+
| text|
+-----+
|nihao|
|hello|
+-----+
 */
df1.show()

val df2: DataFrame = Seq(("a", 1), ("b", 1)).toDF("word", "count")

/*
+----+-----+
|word|count|
+----+-----+
|   a|    1|
|   b|    1|
+----+-----+
 */
df2.show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">通过外部集合创建 <code>DataFrame</code></dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

val df = spark.read
  .option("header", true)
  .csv("dataset/BeijingPM20100101_20151231.csv")
df.show(10)
df.printSchema()</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>不仅可以从 <code>csv</code> 文件创建 <code>DataFrame</code>, 还可以从 <code>Table</code>,
                                            <code>JSON</code>, <code>Parquet</code> 等中创建 <code>DataFrame</code>,
                                            后续会有单独的章节来介绍</p>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">在 <code>DataFrame</code> 上可以使用的常规操作</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="exampleblock">
                                        <div class="content">
                                            <div class="paragraph">
                                                <p>需求: 查看每个月的统计数量</p>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">Step 1: 首先可以打印 <code>DataFrame</code> 的
                                                <code>Schema</code>, 查看其中所包含的列, 以及列的类型
                                            </dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

val df = spark.read
  .option("header", true)
  .csv("dataset/BeijingPM20100101_20151231.csv")

df.printSchema()</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 2: 对于大部分计算来说, 可能不会使用所有的列, 所以可以选择其中某些重要的列</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">...

df.select('year, 'month, 'PM_Dongsi)</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 3: 可以针对某些列进行分组, 后对每组数据通过函数做聚合</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">...

df.select('year, 'month, 'PM_Dongsi)
  .where('PM_Dongsi =!= "Na")
  .groupBy('year, 'month)
  .count()
  .show()</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">使用 <code>SQL</code> 操作 <code>DataFrame</code></dt>
                    </dl>
                </div>
                <div class="sidebarblock">
                    <div class="content">
                        <div class="paragraph">
                            <p>使用 <code>SQL</code> 来操作某个 <code>DataFrame</code> 的话, <code>SQL</code> 中必须要有一个
                                <code>from</code> 子句, 所以需要先将 <code>DataFrame</code> 注册为一张临时表</p>
                        </div>
                        <div class="listingblock">
                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

val df = spark.read
  .option("header", true)
  .csv("dataset/BeijingPM20100101_20151231.csv")

df.createOrReplaceTempView("temp_table")

spark.sql("select year, month, count(*) from temp_table where PM_Dongsi != 'NA' group by year, month")
  .show()</code></pre>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="exampleblock">
                    <div class="title">总结</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p><code>DataFrame</code> 是一个类似于关系型数据库表的函数式组件</p>
                                </li>
                                <li>
                                    <p><code>DataFrame</code> 一般处理结构化数据和半结构化数据</p>
                                </li>
                                <li>
                                    <p><code>DataFrame</code> 具有数据对象的 Schema 信息</p>
                                </li>
                                <li>
                                    <p>可以使用命令式的 <code>API</code> 操作 <code>DataFrame</code>, 同时也可以使用 <code>SQL</code> 操作
                                        <code>DataFrame</code></p>
                                </li>
                                <li>
                                    <p><code>DataFrame</code> 可以由一个已经存在的集合直接创建, 也可以读取外部的数据源来创建</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_6_dataset_和_dataframe_的异同">6. Dataset 和 DataFrame 的异同</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">目标</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>理解 <code>Dataset</code> 和 <code>DataFrame</code> 之间的关系</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="dlist">
                    <dl>
                        <dt class="hdlist1"><code>DataFrame</code> 就是 <code>Dataset</code></dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>根据前面的内容, 可以得到如下信息</p>
                                    </div>
                                    <div class="olist arabic">
                                        <ol class="arabic">
                                            <li>
                                                <p><code>Dataset</code> 中可以使用列来访问数据, <code>DataFrame</code> 也可以</p>
                                            </li>
                                            <li>
                                                <p><code>Dataset</code> 的执行是优化的, <code>DataFrame</code> 也是</p>
                                            </li>
                                            <li>
                                                <p><code>Dataset</code> 具有命令式 <code>API</code>, 同时也可以使用 <code>SQL</code>
                                                    来访问, <code>DataFrame</code> 也可以使用这两种不同的方式访问</p>
                                            </li>
                                        </ol>
                                    </div>
                                    <div class="paragraph">
                                        <p>所以这件事就比较蹊跷了, 两个这么相近的东西为什么会同时出现在 <code>SparkSQL</code> 中呢?</p>
                                    </div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="44fb917304a91eab99d131010448331b"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/44fb917304a91eab99d131010448331b.png" width="600">
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>确实, 这两个组件是同一个东西, <code>DataFrame</code> 是 <code>Dataset</code> 的一种特殊情况, 也就是说
                                            <code>DataFrame</code> 是 <code>Dataset[Row]</code> 的别名</p>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>DataFrame</code> 和 <code>Dataset</code> 所表达的语义不同</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><strong>第一点: <code>DataFrame</code> 表达的含义是一个支持函数式操作的 <code>表</code>, 而 <code>Dataset</code>
                                            表达是是一个类似 <code>RDD</code> 的东西, <code>Dataset</code> 可以处理任何对象</strong></p>
                                    </div>
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">第二点: <code>DataFrame</code> 中所存放的是 <code>Row</code> 对象,
                                                而 <code>Dataset</code> 中可以存放任何类型的对象
                                            </dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val df: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()       <i class="conum" data-value="1"></i><b>(1)</b>

val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS() <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
                                                    </div>
                                                </div>
                                                <div class="colist arabic">
                                                    <table>
                                                        <tr>
                                                            <td><i class="conum" data-value="1"></i><b>1</b></td>
                                                            <td>DataFrame 就是 Dataset[Row]</td>
                                                        </tr>
                                                        <tr>
                                                            <td><i class="conum" data-value="2"></i><b>2</b></td>
                                                            <td>Dataset 的范型可以是任意类型</td>
                                                        </tr>
                                                    </table>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">第三点: <code>DataFrame</code> 的操作方式和 <code>Dataset</code>
                                                是一样的, 但是对于强类型操作而言, 它们处理的类型不同
                                            </dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p><code>DataFrame</code> 在进行强类型操作时候, 例如 <code>map</code>
                                                                算子, 其所处理的数据类型永远是 <code>Row</code></p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
                                                                <pre class="highlightjs highlight"><code
                                                                        class="language-scala hljs" data-lang="scala">df.map( (row: Row) =&gt; Row(row.get(0), row.getAs[Int](1) * 10) )(RowEncoder.apply(df.schema)).show()</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>但是对于 <code>Dataset</code> 来讲, 其中是什么类型, 它就处理什么类型</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
                                                                <pre class="highlightjs highlight"><code
                                                                        class="language-scala hljs" data-lang="scala">ds.map( (item: People) =&gt; People(item.name, item.age * 10) ).show()</code></pre>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">第三点: <code>DataFrame</code> 只能做到运行时类型检查,
                                                <code>Dataset</code> 能做到编译和运行时都有类型检查
                                            </dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="olist arabic">
                                                            <ol class="arabic">
                                                                <li>
                                                                    <p><code>DataFrame</code> 中存放的数据以 <code>Row</code>
                                                                        表示, 一个 <code>Row</code> 代表一行数据, 这和关系型数据库类似</p>
                                                                </li>
                                                                <li>
                                                                    <p><code>DataFrame</code> 在进行 <code>map</code>
                                                                        等操作的时候, <code>DataFrame</code> 不能直接使用 <code>Person</code>
                                                                        这样的 <code>Scala</code> 对象, 所以无法做到编译时检查</p>
                                                                </li>
                                                                <li>
                                                                    <p><code>Dataset</code> 表示的具体的某一类对象, 例如
                                                                        <code>Person</code>, 所以再进行 <code>map</code>
                                                                        等操作的时候, 传入的是具体的某个 <code>Scala</code> 对象,
                                                                        如果调用错了方法, 编译时就会被检查出来</p>
                                                                </li>
                                                            </ol>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
ds.map(person =&gt; person.nameEd) <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="colist arabic">
                                                            <table>
                                                                <tr>
                                                                    <td><i class="conum" data-value="1"></i><b>1</b>
                                                                    </td>
                                                                    <td>这行代码明显报错, 无法通过编译</td>
                                                                </tr>
                                                            </table>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>Row</code> 是什么?</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>Row</code> 对象表示的是一个 <code>行</code></p>
                                    </div>
                                    <div class="paragraph">
                                        <p><code>Row</code> 的操作类似于 <code>Scala</code> 中的 <code>Map</code> 数据类型</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">// 一个对象就是一个对象
val p = People(name = "zhangsan", age = 10)

// 同样一个对象, 还可以通过一个 Row 对象来表示
val row = Row("zhangsan", 10)

// 获取 Row 中的内容
println(row.get(1))
println(row(1))

// 获取时可以指定类型
println(row.getAs[Int](1))

// 同时 Row 也是一个样例类, 可以进行 match
row match {
  case Row(name, age) =&gt; println(name, age)
}</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>DataFrame</code> 和 <code>Dataset</code> 之间可以非常简单的相互转换</dt>
                        <dd>
                            <div class="listingblock">
                                <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val df: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()
val ds_fdf: Dataset[People] = df.as[People]

val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
val df_fds: DataFrame = ds.toDF()</code></pre>
                                </div>
                            </div>
                        </dd>
                    </dl>
                </div>
                <div class="exampleblock">
                    <div class="title">总结</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p><code>DataFrame</code> 就是 <code>Dataset</code>, 他们的方式是一样的, 也都支持 <code>API</code>
                                        和 <code>SQL</code> 两种操作方式</p>
                                </li>
                                <li>
                                    <p><code>DataFrame</code> 只能通过表达式的形式, 或者列的形式来访问数据, 只有 <code>Dataset</code>
                                        支持针对于整个对象的操作</p>
                                </li>
                                <li>
                                    <p><code>DataFrame</code> 中的数据表示为 <code>Row</code>, 是一个行的概念</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_7_数据读写">7. 数据读写</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">目标</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>理解外部数据源的访问框架</p>
                                </li>
                                <li>
                                    <p>掌握常见的数据源读写方式</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_7_1_初识_dataframereader">7.1. 初识 DataFrameReader</h3>
                    <div class="exampleblock">
                        <div class="title">目标</div>
                        <div class="content">
                            <div class="ulist">
                                <ul>
                                    <li>
                                        <p>理解 <code>DataFrameReader</code> 的整体结构和组成</p>
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </div>
                    <div class="paragraph">
                        <p><code>SparkSQL</code> 的一个非常重要的目标就是完善数据读取, 所以 <code>SparkSQL</code> 中增加了一个新的框架, 专门用于读取外部数据源,
                            叫做 <code>DataFrameReader</code></p>
                    </div>
                    <div class="listingblock">
                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.DataFrameReader

val spark: SparkSession = ...

val reader: DataFrameReader = spark.read</code></pre>
                        </div>
                    </div>
                    <div class="paragraph">
                        <p><code>DataFrameReader</code> 由如下几个组件组成</p>
                    </div>
                    <table class="tableblock frame-all grid-all stretch">
                        <colgroup>
                            <col style="width: 50%;">
                            <col style="width: 50%;">
                        </colgroup>
                        <thead>
                        <tr>
                            <th class="tableblock halign-left valign-top">组件</th>
                            <th class="tableblock halign-left valign-top">解释</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>schema</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">结构信息, 因为
                                <code>Dataset</code> 是有结构的, 所以在读取数据的时候, 就需要有 <code>Schema</code> 信息, 有可能是从外部数据源获取的,
                                也有可能是指定的</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>option</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">连接外部数据源的参数, 例如 <code>JDBC</code>
                                的 <code>URL</code>, 或者读取 <code>CSV</code> 文件是否引入 <code>Header</code> 等</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>format</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">外部数据源的格式, 例如
                                <code>csv</code>, <code>jdbc</code>, <code>json</code> 等</p></td>
                        </tr>
                        </tbody>
                    </table>
                    <div class="paragraph">
                        <p><code>DataFrameReader</code> 有两种访问方式, 一种是使用 <code>load</code> 方法加载, 使用 <code>format</code>
                            指定加载格式, 还有一种是使用封装方法, 类似 <code>csv</code>, <code>json</code>, <code>jdbc</code> 等</p>
                    </div>
                    <div class="listingblock">
                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.DataFrame

val spark: SparkSession = ...

// 使用 load 方法
val fromLoad: DataFrame = spark
  .read
  .format("csv")
  .option("header", true)
  .option("inferSchema", true)
  .load("dataset/BeijingPM20100101_20151231.csv")

// Using format-specific load operator
val fromCSV: DataFrame = spark
  .read
  .option("header", true)
  .option("inferSchema", true)
  .csv("dataset/BeijingPM20100101_20151231.csv")</code></pre>
                        </div>
                    </div>
                    <div class="paragraph">
                        <p>但是其实这两种方式本质上一样, 因为类似 <code>csv</code> 这样的方式只是 <code>load</code> 的封装</p>
                    </div>
                    <div class="imageblock">
                        <div class="content">
                            <img alt="e8af7d7e5ec256de27b2e40c8449a906"
                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/e8af7d7e5ec256de27b2e40c8449a906.png">
                        </div>
                    </div>
                    <div class="admonitionblock note">
                        <table>
                            <tr>
                                <td class="icon">
                                    <i class="fa icon-note" title="Note"></i>
                                </td>
                                <td class="content">
                                    <div class="paragraph">
                                        <p>如果使用 <code>load</code> 方法加载数据, 但是没有指定 <code>format</code> 的话, 默认是按照 <code>Parquet</code>
                                            文件格式读取</p>
                                    </div>
                                    <div class="paragraph">
                                        <p>也就是说, <code>SparkSQL</code> 默认的读取格式是 <code>Parquet</code></p>
                                    </div>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="exampleblock">
                        <div class="title">总结</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p>使用 <code>spark.read</code> 可以获取 SparkSQL 中的外部数据源访问框架
                                            <code>DataFrameReader</code></p>
                                    </li>
                                    <li>
                                        <p><code>DataFrameReader</code> 有三个组件 <code>format</code>, <code>schema</code>,
                                            <code>option</code></p>
                                    </li>
                                    <li>
                                        <p><code>DataFrameReader</code> 有两种使用方式, 一种是使用 <code>load</code> 加
                                            <code>format</code> 指定格式, 还有一种是使用封装方法 <code>csv</code>, <code>json</code> 等
                                        </p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_7_2_初识_dataframewriter">7.2. 初识 DataFrameWriter</h3>
                    <div class="exampleblock">
                        <div class="title">目标</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p>理解 <code>DataFrameWriter</code> 的结构</p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                    <div class="paragraph">
                        <p>对于 <code>ETL</code> 来说, 数据保存和数据读取一样重要, 所以 <code>SparkSQL</code> 中增加了一个新的数据写入框架, 叫做 <code>DataFrameWriter</code>
                        </p>
                    </div>
                    <div class="listingblock">
                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = ...

val df = spark.read
      .option("header", true)
      .csv("dataset/BeijingPM20100101_20151231.csv")

val writer: DataFrameWriter[Row] = df.write</code></pre>
                        </div>
                    </div>
                    <div class="paragraph">
                        <p><code>DataFrameWriter</code> 中由如下几个部分组成</p>
                    </div>
                    <table class="tableblock frame-all grid-all stretch">
                        <colgroup>
                            <col style="width: 50%;">
                            <col style="width: 50%;">
                        </colgroup>
                        <thead>
                        <tr>
                            <th class="tableblock halign-left valign-top">组件</th>
                            <th class="tableblock halign-left valign-top">解释</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>source</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">写入目标, 文件格式等, 通过 <code>format</code>
                                方法设定</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>mode</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">写入模式, 例如一张表已经存在, 如果通过
                                <code>DataFrameWriter</code> 向这张表中写入数据, 是覆盖表呢, 还是向表中追加呢? 通过 <code>mode</code> 方法设定</p>
                            </td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>extraOptions</code></p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">外部参数, 例如
                                <code>JDBC</code> 的 <code>URL</code>, 通过 <code>options</code>, <code>option</code> 设定
                            </p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>partitioningColumns</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">类似 <code>Hive</code>
                                的分区, 保存表的时候使用, 这个地方的分区不是 <code>RDD</code> 的分区, 而是文件的分区, 或者表的分区, 通过
                                <code>partitionBy</code> 设定</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>bucketColumnNames</code></p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">类似 <code>Hive</code>
                                的分桶, 保存表的时候使用, 通过 <code>bucketBy</code> 设定</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>sortColumnNames</code></p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">用于排序的列, 通过
                                <code>sortBy</code> 设定</p></td>
                        </tr>
                        </tbody>
                    </table>
                    <div class="paragraph">
                        <p><code>mode</code> 指定了写入模式, 例如覆盖原数据集, 或者向原数据集合中尾部添加等</p>
                    </div>
                    <table class="tableblock frame-all grid-all stretch">
                        <colgroup>
                            <col style="width: 33.3333%;">
                            <col style="width: 33.3333%;">
                            <col style="width: 33.3334%;">
                        </colgroup>
                        <thead>
                        <tr>
                            <th class="tableblock halign-left valign-top"><code>Scala</code> 对象表示</th>
                            <th class="tableblock halign-left valign-top">字符串表示</th>
                            <th class="tableblock halign-left valign-top">解释</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>SaveMode.ErrorIfExists</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>"error"</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">将 <code>DataFrame</code>
                                保存到 <code>source</code> 时, 如果目标已经存在, 则报错</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>SaveMode.Append</code></p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>"append"</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">将 <code>DataFrame</code>
                                保存到 <code>source</code> 时, 如果目标已经存在, 则添加到文件或者 <code>Table</code> 中</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>SaveMode.Overwrite</code></p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>"overwrite"</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">将 <code>DataFrame</code>
                                保存到 <code>source</code> 时, 如果目标已经存在, 则使用 <code>DataFrame</code> 中的数据完全覆盖目标</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>SaveMode.Ignore</code></p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>"ignore"</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">将 <code>DataFrame</code>
                                保存到 <code>source</code> 时, 如果目标已经存在, 则不会保存 <code>DataFrame</code> 数据, 并且也不修改目标数据集, 类似于
                                <code>CREATE TABLE IF NOT EXISTS</code></p></td>
                        </tr>
                        </tbody>
                    </table>
                    <div class="paragraph">
                        <p><code>DataFrameWriter</code> 也有两种使用方式, 一种是使用 <code>format</code> 配合 <code>save</code>,
                            还有一种是使用封装方法, 例如 <code>csv</code>, <code>json</code>, <code>saveAsTable</code> 等</p>
                    </div>
                    <div class="listingblock">
                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = ...

val df = spark.read
  .option("header", true)
  .csv("dataset/BeijingPM20100101_20151231.csv")

// 使用 save 保存, 使用 format 设置文件格式
df.write.format("json").save("dataset/beijingPM")

// 使用 json 保存, 因为方法是 json, 所以隐含的 format 是 json
df.write.json("dataset/beijingPM1")</code></pre>
                        </div>
                    </div>
                    <div class="admonitionblock note">
                        <table>
                            <tr>
                                <td class="icon">
                                    <i class="fa icon-note" title="Note"></i>
                                </td>
                                <td class="content">
                                    <div class="paragraph">
                                        <p>默认没有指定 <code>format</code>, 默认的 <code>format</code> 是 <code>Parquet</code>
                                        </p>
                                    </div>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="exampleblock">
                        <div class="title">总结</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p>类似 <code>DataFrameReader</code>, <code>Writer</code> 中也有 <code>format</code>,
                                            <code>options</code>, 另外 <code>schema</code> 是包含在 <code>DataFrame</code> 中的
                                        </p>
                                    </li>
                                    <li>
                                        <p><code>DataFrameWriter</code> 中还有一个很重要的概念叫做 <code>mode</code>, 指定写入模式,
                                            如果目标集合已经存在时的行为</p>
                                    </li>
                                    <li>
                                        <p><code>DataFrameWriter</code> 可以将数据保存到 <code>Hive</code> 表中, 所以也可以指定分区和分桶信息
                                        </p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_7_3_读写_parquet_格式文件">7.3. 读写 Parquet 格式文件</h3>
                    <div class="exampleblock">
                        <div class="title">目标</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p>理解 <code>Spark</code> 读写 <code>Parquet</code> 文件的语法</p>
                                    </li>
                                    <li>
                                        <p>理解 <code>Spark</code> 读写 <code>Parquet</code> 文件的时候对于分区的处理</p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                    <div class="dlist">
                        <dl>
                            <dt class="hdlist1">什么时候会用到 <code>Parquet</code> ?</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="00a2a56f725d86b5c27463f109c43d8c"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/00a2a56f725d86b5c27463f109c43d8c.png">
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p>在 <code>ETL</code> 中, <code>Spark</code> 经常扮演 <code>T</code> 的职务,
                                                也就是进行数据清洗和数据转换.</p>
                                        </div>
                                        <div class="paragraph">
                                            <p>为了能够保存比较复杂的数据, 并且保证性能和压缩率, 通常使用 <code>Parquet</code> 是一个比较不错的选择.</p>
                                        </div>
                                        <div class="paragraph">
                                            <p>所以外部系统收集过来的数据, 有可能会使用 <code>Parquet</code>, 而 <code>Spark</code>
                                                进行读取和转换的时候, 就需要支持对 <code>Parquet</code> 格式的文件的支持.</p>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">使用代码读写 <code>Parquet</code> 文件</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>默认不指定 <code>format</code> 的时候, 默认就是读写 <code>Parquet</code> 格式的文件</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

val df = spark.read
  .option("header", value = true)
  .csv("dataset/911.csv")

// 保存 Parquet 文件
df.write.mode("override").save("dataset/911.parquet")

// 读取 Parquet 文件
val dfFromParquet = spark.read.parquet("dataset/911.parquet")
dfFromParquet.createOrReplaceTempView("911")

spark.sql("select * from 911 where zip &gt; 19000 and zip &lt; 19400").show()</code></pre>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">写入 <code>Parquet</code> 的时候可以指定分区</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p><code>Spark</code> 在写入文件的时候是支持分区的, 可以像 <code>Hive</code> 一样设置某个列为分区列</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

// 从 CSV 中读取内容
val dfFromParquet = spark.read.option("header", value = true).csv("dataset/BeijingPM20100101_20151231.csv")

// 保存为 Parquet 格式文件, 不指定 format 默认就是 Parquet
dfFromParquet.write.partitionBy("year", "month").save("dataset/beijing_pm")</code></pre>
                                            </div>
                                        </div>
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="67314102d7b36b791b04bafeb5d0d3e8"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/67314102d7b36b791b04bafeb5d0d3e8.png" width="300">
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                        </dl>
                    </div>
                    <div class="admonitionblock note">
                        <table>
                            <tr>
                                <td class="icon">
                                    <i class="fa icon-note" title="Note"></i>
                                </td>
                                <td class="content">
                                    <div class="paragraph">
                                        <p>这个地方指的分区是类似 <code>Hive</code> 中表分区的概念, 而不是 <code>RDD</code> 分布式分区的含义</p>
                                    </div>
                                </td>
                            </tr>
                        </table>
                    </div>
                    <div class="dlist">
                        <dl>
                            <dt class="hdlist1">分区发现</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>在读取常见文件格式的时候, <code>Spark</code> 会自动的进行分区发现, 分区自动发现的时候, 会将文件名中的分区信息当作一列.
                                                例如 如果按照性别分区, 那么一般会生成两个文件夹 <code>gender=male</code> 和
                                                <code>gender=female</code>, 那么在使用 <code>Spark</code> 读取的时候, 会自动发现这个分区信息,
                                                并且当作列放入创建的 <code>DataFrame</code> 中</p>
                                        </div>
                                        <div class="paragraph">
                                            <p>使用代码证明这件事可以有两个步骤, 第一步先读取某个分区的单独一个文件并打印其 <code>Schema</code> 信息,
                                                第二步读取整个数据集所有分区并打印 <code>Schema</code> 信息, 和第一步做比较就可以确定</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = ...

val partDF = spark.read.load("dataset/beijing_pm/year=2010/month=1") <i class="conum" data-value="1"></i><b>(1)</b>
partDF.printSchema()</code></pre>
                                            </div>
                                        </div>
                                        <div class="colist arabic">
                                            <table>
                                                <tr>
                                                    <td><i class="conum" data-value="1"></i><b>1</b></td>
                                                    <td>把分区的数据集中的某一个区单做一整个数据集读取, 没有分区信息, 自然也不会进行分区发现</td>
                                                </tr>
                                            </table>
                                        </div>
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="dbb274b7fcdfd82c3a3922dfa6bfb29e"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/dbb274b7fcdfd82c3a3922dfa6bfb29e.png" width="600">
                                            </div>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val df = spark.read.load("dataset/beijing_pm") <i
        class="conum" data-value="1"></i><b>(1)</b>
df.printSchema()</code></pre>
                                            </div>
                                        </div>
                                        <div class="colist arabic">
                                            <table>
                                                <tr>
                                                    <td><i class="conum" data-value="1"></i><b>1</b></td>
                                                    <td>此处读取的是整个数据集, 会进行分区发现, DataFrame 中会包含分去列</td>
                                                </tr>
                                            </table>
                                        </div>
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="84353e6ed2cf479b82b4d2e4e2b6c3c2"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/84353e6ed2cf479b82b4d2e4e2b6c3c2.png" width="600">
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                        </dl>
                    </div>
                    <table class="tableblock frame-all grid-all stretch">
                        <caption class="title">Table 1. <code>SparkSession</code> 中有关 <code>Parquet</code> 的配置</caption>
                        <colgroup>
                            <col style="width: 33.3333%;">
                            <col style="width: 33.3333%;">
                            <col style="width: 33.3334%;">
                        </colgroup>
                        <thead>
                        <tr>
                            <th class="tableblock halign-left valign-top">配置</th>
                            <th class="tableblock halign-left valign-top">默认值</th>
                            <th class="tableblock halign-left valign-top">含义</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>spark.sql.parquet.binaryAsString</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">一些其他
                                <code>Parquet</code> 生产系统, 不区分字符串类型和二进制类型, 该配置告诉 <code>SparkSQL</code>
                                将二进制数据解释为字符串以提供与这些系统的兼容性</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>spark.sql.parquet.int96AsTimestamp</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">一些其他
                                <code>Parquet</code> 生产系统, 将 <code>Timestamp</code> 存为 <code>INT96</code>, 该配置告诉 <code>SparkSQL</code>
                                将 <code>INT96</code> 解析为 <code>Timestamp</code></p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>spark.sql.parquet.cacheMetadata</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">打开 Parquet 元数据的缓存,
                                可以加快查询静态数据</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>spark.sql.parquet.compression.codec</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>snappy</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">压缩方式, 可选 <code>uncompressed</code>,
                                <code>snappy</code>, <code>gzip</code>, <code>lzo</code></p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>spark.sql.parquet.mergeSchema</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>false</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">当为 true 时, Parquet
                                数据源会合并从所有数据文件收集的 Schemas 和数据, 因为这个操作开销比较大, 所以默认关闭</p></td>
                        </tr>
                        <tr>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>spark.sql.optimizer.metadataOnly</code>
                            </p></td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>true</code></p>
                            </td>
                            <td class="tableblock halign-left valign-top"><p class="tableblock">如果为 <code>true</code>,
                                会通过原信息来生成分区列, 如果为 <code>false</code> 则就是通过扫描整个数据集来确定</p></td>
                        </tr>
                        </tbody>
                    </table>
                    <div class="exampleblock">
                        <div class="title">总结</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p><code>Spark</code> 不指定 <code>format</code> 的时候默认就是按照 <code>Parquet</code>
                                            的格式解析文件</p>
                                    </li>
                                    <li>
                                        <p><code>Spark</code> 在读取 <code>Parquet</code> 文件的时候会自动的发现 <code>Parquet</code>
                                            的分区和分区字段</p>
                                    </li>
                                    <li>
                                        <p><code>Spark</code> 在写入 <code>Parquet</code> 文件的时候如果设置了分区字段, 会自动的按照分区存储</p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_7_4_读写_json_格式文件">7.4. 读写 JSON 格式文件</h3>
                    <div class="exampleblock">
                        <div class="title">目标</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p>理解 <code>JSON</code> 的使用场景</p>
                                    </li>
                                    <li>
                                        <p>能够使用 <code>Spark</code> 读取处理 <code>JSON</code> 格式文件</p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                    <div class="dlist">
                        <dl>
                            <dt class="hdlist1">什么时候会用到 <code>JSON</code> ?</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="00a2a56f725d86b5c27463f109c43d8c"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/00a2a56f725d86b5c27463f109c43d8c.png">
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p>在 <code>ETL</code> 中, <code>Spark</code> 经常扮演 <code>T</code> 的职务,
                                                也就是进行数据清洗和数据转换.</p>
                                        </div>
                                        <div class="paragraph">
                                            <p>在业务系统中, <code>JSON</code> 是一个非常常见的数据格式, 在前后端交互的时候也往往会使用 <code>JSON</code>,
                                                所以从业务系统获取的数据很大可能性是使用 <code>JSON</code> 格式, 所以就需要 <code>Spark</code> 能够支持
                                                JSON 格式文件的读取</p>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">读写 <code>JSON</code> 文件</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>将要 <code>Dataset</code> 保存为 <code>JSON</code> 格式的文件比较简单, 是 <code>DataFrameWriter</code>
                                                的一个常规使用</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

val dfFromParquet = spark.read.load("dataset/beijing_pm")

// 将 DataFrame 保存为 JSON 格式的文件
dfFromParquet.repartition(1)        <i class="conum" data-value="1"></i><b>(1)</b>
  .write.format("json")
  .save("dataset/beijing_pm_json")</code></pre>
                                            </div>
                                        </div>
                                        <div class="colist arabic">
                                            <table>
                                                <tr>
                                                    <td><i class="conum" data-value="1"></i><b>1</b></td>
                                                    <td>如果不重新分区, 则会为 <code>DataFrame</code> 底层的 <code>RDD</code>
                                                        的每个分区生成一个文件, 为了保持只有一个输出文件, 所以重新分区
                                                    </td>
                                                </tr>
                                            </table>
                                        </div>
                                        <div class="admonitionblock note">
                                            <table>
                                                <tr>
                                                    <td class="icon">
                                                        <i class="fa icon-note" title="Note"></i>
                                                    </td>
                                                    <td class="content">
                                                        <div class="paragraph">
                                                            <p>保存为 <code>JSON</code> 格式的文件有一个细节需要注意, 这个
                                                                <code>JSON</code> 格式的文件中, 每一行是一个独立的 <code>JSON</code>,
                                                                但是整个文件并不只是一个 <code>JSON</code> 字符串, 所以这种文件格式很多时候被成为
                                                                <code>JSON Line</code> 文件, 有时候后缀名也会变为 <code>jsonl</code>
                                                            </p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="title">beijing_pm.jsonl</div>
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{"day":"1","hour":"0","season":"1","year":2013,"month":3}
{"day":"1","hour":"1","season":"1","year":2013,"month":3}
{"day":"1","hour":"2","season":"1","year":2013,"month":3}</code></pre>
                                                            </div>
                                                        </div>
                                                    </td>
                                                </tr>
                                            </table>
                                        </div>
                                        <div class="paragraph">
                                            <p>也可以通过 <code>DataFrameReader</code> 读取一个 <code>JSON Line</code> 文件</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = ...

val dfFromJSON = spark.read.json("dataset/beijing_pm_json")
dfFromJSON.show()</code></pre>
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p><code>JSON</code> 格式的文件是有结构信息的, 也就是 <code>JSON</code> 中的字段是有类型的, 例如
                                                <code>"name": "zhangsan"</code> 这样由双引号包裹的 <code>Value</code>, 就是字符串类型, 而
                                                <code>"age": 10</code> 这种没有双引号包裹的就是数字类型, 当然, 也可以是布尔型 <code>"has_wife":
                                                    true</code></p>
                                        </div>
                                        <div class="paragraph">
                                            <p><code>Spark</code> 读取 <code>JSON Line</code> 文件的时候, 会自动的推断类型信息</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = ...

val dfFromJSON = spark.read.json("dataset/beijing_pm_json")

dfFromJSON.printSchema()</code></pre>
                                            </div>
                                        </div>
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="e8a53ef37bbf6675525d1a844f8648f1"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/e8a53ef37bbf6675525d1a844f8648f1.png" width="600">
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1"><code>Spark</code> 可以从一个保存了 <code>JSON</code> 格式字符串的 <code>Dataset[String]</code>
                                中读取 <code>JSON</code> 信息, 转为 <code>DataFrame</code></dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>这种情况其实还是比较常见的, 例如如下的流程</p>
                                        </div>
                                        <div class="imageblock">
                                            <div class="content">
                                                <img alt="da6f1c7f8d98691117a173e03bfdf18f"
                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/da6f1c7f8d98691117a173e03bfdf18f.png">
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p>假设业务系统通过 <code>Kafka</code> 将数据流转进入大数据平台, 这个时候可能需要使用 <code>RDD</code> 或者
                                                <code>Dataset</code> 来读取其中的内容, 这个时候一条数据就是一个 <code>JSON</code> 格式的字符串,
                                                如何将其转为 <code>DataFrame</code> 或者 <code>Dataset[Object]</code> 这样具有
                                                <code>Schema</code> 的数据集呢? 使用如下代码就可以</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark: SparkSession = ...

import spark.implicits._

val peopleDataset = spark.createDataset(
  """{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}""" :: Nil)

spark.read.json(peopleDataset).show()</code></pre>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                        </dl>
                    </div>
                    <div class="exampleblock">
                        <div class="title">总结</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p><code>JSON</code> 通常用于系统间的交互, <code>Spark</code> 经常要读取 <code>JSON</code>
                                            格式文件, 处理, 放在另外一处</p>
                                    </li>
                                    <li>
                                        <p>使用 <code>DataFrameReader</code> 和 <code>DataFrameWriter</code> 可以轻易的读取和写入
                                            <code>JSON</code>, 并且会自动处理数据类型信息</p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_7_5_访问_hive">7.5. 访问 Hive</h3>
                    <div class="exampleblock">
                        <div class="title">导读</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p>整合 <code>SparkSQL</code> 和 <code>Hive</code>, 使用 <code>Hive</code> 的 <code>MetaStore</code>
                                            元信息库</p>
                                    </li>
                                    <li>
                                        <p>使用 <code>SparkSQL</code> 查询 <code>Hive</code> 表</p>
                                    </li>
                                    <li>
                                        <p>案例, 使用常见 <code>HiveSQL</code></p>
                                    </li>
                                    <li>
                                        <p>写入内容到 <code>Hive</code> 表</p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                    <div class="sect3">
                        <h4 id="_7_5_1_sparksql_整合_hive">7.5.1. SparkSQL 整合 Hive</h4>
                        <div class="exampleblock">
                            <div class="title">导读</div>
                            <div class="content">
                                <div class="olist arabic">
                                    <ol class="arabic">
                                        <li>
                                            <p>开启 <code>Hive</code> 的 <code>MetaStore</code> 独立进程</p>
                                        </li>
                                        <li>
                                            <p>整合 <code>SparkSQL</code> 和 <code>Hive</code> 的 <code>MetaStore</code></p>
                                        </li>
                                    </ol>
                                </div>
                            </div>
                        </div>
                        <div class="paragraph">
                            <p>和一个文件格式不同, <code>Hive</code> 是一个外部的数据存储和查询引擎, 所以如果 <code>Spark</code> 要访问
                                <code>Hive</code> 的话, 就需要先整合 <code>Hive</code></p>
                        </div>
                        <div class="dlist">
                            <dl>
                                <dt class="hdlist1">整合什么 ?</dt>
                                <dd>
                                    <div class="sidebarblock">
                                        <div class="content">
                                            <div class="paragraph">
                                                <p>如果要讨论 <code>SparkSQL</code> 如何和 <code>Hive</code> 进行整合, 首要考虑的事应该是
                                                    <code>Hive</code> 有什么, 有什么就整合什么就可以</p>
                                            </div>
                                            <div class="ulist">
                                                <ul>
                                                    <li>
                                                        <p><code>MetaStore</code>, 元数据存储</p>
                                                        <div class="paragraph">
                                                            <p><code>SparkSQL</code> 内置的有一个 <code>MetaStore</code>,
                                                                通过嵌入式数据库 <code>Derby</code> 保存元信息, 但是对于生产环境来说, 还是应该使用
                                                                <code>Hive</code> 的 <code>MetaStore</code>, 一是更成熟, 功能更强,
                                                                二是可以使用 <code>Hive</code> 的元信息</p>
                                                        </div>
                                                    </li>
                                                    <li>
                                                        <p>查询引擎</p>
                                                        <div class="paragraph">
                                                            <p><code>SparkSQL</code> 内置了 <code>HiveSQL</code> 的支持,
                                                                所以无需整合</p>
                                                        </div>
                                                    </li>
                                                </ul>
                                            </div>
                                        </div>
                                    </div>
                                </dd>
                                <dt class="hdlist1">为什么要开启 <code>Hive</code> 的 <code>MetaStore</code></dt>
                                <dd>
                                    <div class="sidebarblock">
                                        <div class="content">
                                            <div class="paragraph">
                                                <p><code>Hive</code> 的 <code>MetaStore</code> 是一个 <code>Hive</code> 的组件,
                                                    一个 <code>Hive</code> 提供的程序, 用以保存和访问表的元数据, 整个 <code>Hive</code>
                                                    的结构大致如下</p>
                                            </div>
                                            <div class="imageblock">
                                                <div class="content">
                                                    <img alt="20190523011946"
                                                         src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190523011946.png">
                                                </div>
                                            </div>
                                            <div class="paragraph">
                                                <p>由上图可知道, 其实 <code>Hive</code> 中主要的组件就三个, <code>HiveServer2</code>
                                                    负责接受外部系统的查询请求, 例如 <code>JDBC</code>, <code>HiveServer2</code>
                                                    接收到查询请求后, 交给 <code>Driver</code> 处理, <code>Driver</code> 会首先去询问
                                                    <code>MetaStore</code> 表在哪存, 后 <code>Driver</code> 程序通过
                                                    <code>MR</code> 程序来访问 <code>HDFS</code> 从而获取结果返回给查询请求者</p>
                                            </div>
                                            <div class="paragraph">
                                                <p>而 <code>Hive</code> 的 <code>MetaStore</code> 对 <code>SparkSQL</code>
                                                    的意义非常重大, 如果 <code>SparkSQL</code> 可以直接访问 <code>Hive</code> 的 <code>MetaStore</code>,
                                                    则理论上可以做到和 <code>Hive</code> 一样的事情, 例如通过 <code>Hive</code> 表查询数据</p>
                                            </div>
                                            <div class="paragraph">
                                                <p>而 Hive 的 MetaStore 的运行模式有三种</p>
                                            </div>
                                            <div class="ulist">
                                                <ul>
                                                    <li>
                                                        <p>内嵌 <code>Derby</code> 数据库模式</p>
                                                        <div class="paragraph">
                                                            <p>这种模式不必说了, 自然是在测试的时候使用, 生产环境不太可能使用嵌入式数据库, 一是不稳定, 二是这个
                                                                <code>Derby</code> 是单连接的, 不支持并发</p>
                                                        </div>
                                                    </li>
                                                    <li>
                                                        <p><code>Local</code> 模式</p>
                                                        <div class="paragraph">
                                                            <p><code>Local</code> 和 <code>Remote</code> 都是访问
                                                                <code>MySQL</code> 数据库作为存储元数据的地方, 但是 <code>Local</code>
                                                                模式的 <code>MetaStore</code> 没有独立进程, 依附于
                                                                <code>HiveServer2</code> 的进程</p>
                                                        </div>
                                                    </li>
                                                    <li>
                                                        <p><code>Remote</code> 模式</p>
                                                        <div class="paragraph">
                                                            <p>和 <code>Loca</code> 模式一样, 访问 <code>MySQL</code> 数据库存放元数据,
                                                                但是 <code>Remote</code> 的 <code>MetaStore</code>
                                                                运行在独立的进程中</p>
                                                        </div>
                                                    </li>
                                                </ul>
                                            </div>
                                            <div class="paragraph">
                                                <p>我们显然要选择 <code>Remote</code> 模式, 因为要让其独立运行, 这样才能让
                                                    <code>SparkSQL</code> 一直可以访问</p>
                                            </div>
                                        </div>
                                    </div>
                                </dd>
                                <dt class="hdlist1"><code>Hive</code> 开启 <code>MetaStore</code></dt>
                                <dd>
                                    <div class="sidebarblock">
                                        <div class="content">
                                            <div class="dlist">
                                                <dl>
                                                    <dt class="hdlist1"><code>Step 1</code>: 修改
                                                        <code>hive-site.xml</code></dt>
                                                    <dd>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;property&gt;
  &lt;name&gt;hive.metastore.warehouse.dir&lt;/name&gt;
  &lt;value&gt;/user/hive/warehouse&lt;/value&gt;
&lt;/property&gt;

&lt;property&gt;
  &lt;name&gt;javax.jdo.option.ConnectionURL&lt;/name&gt;
  &lt;value&gt;jdbc:mysql://node01:3306/hive?createDatabaseIfNotExist=true&lt;/value&gt;
&lt;/property&gt;

&lt;property&gt;
  &lt;name&gt;javax.jdo.option.ConnectionDriverName&lt;/name&gt;
  &lt;value&gt;com.mysql.jdbc.Driver&lt;/value&gt;
&lt;/property&gt;

&lt;property&gt;
  &lt;name&gt;javax.jdo.option.ConnectionUserName&lt;/name&gt;
  &lt;value&gt;username&lt;/value&gt;
&lt;/property&gt;

&lt;property&gt;
  &lt;name&gt;javax.jdo.option.ConnectionPassword&lt;/name&gt;
  &lt;value&gt;password&lt;/value&gt;
&lt;/property&gt;

&lt;property&gt;
  &lt;name&gt;hive.metastore.local&lt;/name&gt;
  &lt;value&gt;false&lt;/value&gt;
&lt;/property&gt;

&lt;property&gt;
  &lt;name&gt;hive.metastore.uris&lt;/name&gt;
  &lt;value&gt;thrift://node01:9083&lt;/value&gt;  //当前服务器
&lt;/property&gt;</code></pre>
                                                            </div>
                                                        </div>
                                                    </dd>
                                                    <dt class="hdlist1"><code>Step 2</code>: 启动 <code>Hive
                                                        MetaStore</code></dt>
                                                    <dd>
                                                        <div class="listingblock">
                                                            <div class="content">
                                                                <pre class="highlightjs highlight"><code
                                                                        class="language-text hljs" data-lang="text">nohup /export/servers/hive/bin/hive --service metastore 2&gt;&amp;1 &gt;&gt; /var/log.log &amp;</code></pre>
                                                            </div>
                                                        </div>
                                                    </dd>
                                                </dl>
                                            </div>
                                        </div>
                                    </div>
                                </dd>
                                <dt class="hdlist1"><code>SparkSQL</code> 整合 <code>Hive</code> 的 <code>MetaStore</code>
                                </dt>
                                <dd>
                                    <div class="sidebarblock">
                                        <div class="content">
                                            <div class="paragraph">
                                                <p>即使不去整合 <code>MetaStore</code>, <code>Spark</code> 也有一个内置的 <code>MateStore</code>,
                                                    使用 <code>Derby</code> 嵌入式数据库保存数据, 但是这种方式不适合生产环境, 因为这种模式同一时间只能有一个
                                                    <code>SparkSession</code> 使用, 所以生产环境更推荐使用 <code>Hive</code> 的 <code>MetaStore</code>
                                                </p>
                                            </div>
                                            <div class="paragraph">
                                                <p><code>SparkSQL</code> 整合 <code>Hive</code> 的 <code>MetaStore</code>
                                                    主要思路就是要通过配置能够访问它, 并且能够使用 <code>HDFS</code> 保存 <code>WareHouse</code>,
                                                    这些配置信息一般存在于 <code>Hadoop</code> 和 <code>HDFS</code> 的配置文件中, 所以可以直接拷贝
                                                    <code>Hadoop</code> 和 <code>Hive</code> 的配置文件到 <code>Spark</code>
                                                    的配置目录</p>
                                            </div>
                                            <div class="listingblock">
                                                <div class="content">
<pre class="highlightjs highlight"><code class="language-text hljs" data-lang="text">cd /export/servers/hadoop/etc/hadoop
cp hive-site.xml core-site.xml hdfs-site.xml /export/servers/spark/conf/ <i class="conum" data-value="1"></i><b>(1)</b> <i
            class="conum" data-value="2"></i><b>(2)</b> <i class="conum" data-value="3"></i><b>(3)</b>

scp -r /export/servers/spark/conf node02:/export/servers/spark/conf
scp -r /export/servers/spark/conf node03:/export/servers/spark/conf</code></pre>
                                                </div>
                                            </div>
                                            <div class="colist arabic">
                                                <table>
                                                    <tr>
                                                        <td><i class="conum" data-value="1"></i><b>1</b></td>
                                                        <td><code>Spark</code> 需要 <code>hive-site.xml</code> 的原因是, 要读取
                                                            <code>Hive</code> 的配置信息, 主要是元数据仓库的位置等信息
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td><i class="conum" data-value="2"></i><b>2</b></td>
                                                        <td><code>Spark</code> 需要 <code>core-site.xml</code> 的原因是,
                                                            要读取安全有关的配置
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td><i class="conum" data-value="3"></i><b>3</b></td>
                                                        <td><code>Spark</code> 需要 <code>hdfs-site.xml</code> 的原因是,
                                                            有可能需要在 <code>HDFS</code> 中放置表文件, 所以需要 <code>HDFS</code> 的配置
                                                        </td>
                                                    </tr>
                                                </table>
                                            </div>
                                        </div>
                                    </div>
                                </dd>
                            </dl>
                        </div>
                        <div class="admonitionblock note">
                            <table>
                                <tr>
                                    <td class="icon">
                                        <i class="fa icon-note" title="Note"></i>
                                    </td>
                                    <td class="content">
                                        <div class="paragraph">
                                            <p>如果不希望通过拷贝文件的方式整合 Hive, 也可以在 SparkSession 启动的时候, 通过指定 Hive 的 MetaStore
                                                的位置来访问, 但是更推荐整合的方式</p>
                                        </div>
                                    </td>
                                </tr>
                            </table>
                        </div>
                    </div>
                    <div class="sect3">
                        <h4 id="_7_5_2_访问_hive_表">7.5.2. 访问 Hive 表</h4>
                        <div class="exampleblock">
                            <div class="title">导读</div>
                            <div class="content">
                                <div class="olist arabic">
                                    <ol class="arabic">
                                        <li>
                                            <p>在 <code>Hive</code> 中创建表</p>
                                        </li>
                                        <li>
                                            <p>使用 <code>SparkSQL</code> 访问 <code>Hive</code> 中已经存在的表</p>
                                        </li>
                                        <li>
                                            <p>使用 <code>SparkSQL</code> 创建 <code>Hive</code> 表</p>
                                        </li>
                                        <li>
                                            <p>使用 <code>SparkSQL</code> 修改 <code>Hive</code> 表中的数据</p>
                                        </li>
                                    </ol>
                                </div>
                            </div>
                        </div>
                        <div class="dlist">
                            <dl>
                                <dt class="hdlist1">在 <code>Hive</code> 中创建表</dt>
                                <dd>
                                    <div class="sidebarblock">
                                        <div class="content">
                                            <div class="paragraph">
                                                <p>第一步, 需要先将文件上传到集群中, 使用如下命令上传到 <code>HDFS</code> 中</p>
                                            </div>
                                            <div class="listingblock">
                                                <div class="content">
<pre class="highlightjs highlight"><code class="language-text hljs" data-lang="text">hdfs dfs -mkdir -p /dataset
hdfs dfs -put studenttabl10k /dataset/</code></pre>
                                                </div>
                                            </div>
                                            <div class="paragraph">
                                                <p>第二步, 使用 <code>Hive</code> 或者 <code>Beeline</code> 执行如下
                                                    <code>SQL</code></p>
                                            </div>
                                            <div class="listingblock">
                                                <div class="content">
<pre class="highlightjs highlight"><code class="language-sql hljs" data-lang="sql">CREATE DATABASE IF NOT EXISTS spark_integrition;

USE spark_integrition;

CREATE EXTERNAL TABLE student
(
  name  STRING,
  age   INT,
  gpa   string
)
ROW FORMAT DELIMITED
  FIELDS TERMINATED BY '\t'
  LINES TERMINATED BY '\n'
STORED AS TEXTFILE
LOCATION '/dataset/hive';

LOAD DATA INPATH '/dataset/studenttab10k' OVERWRITE INTO TABLE student;</code></pre>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </dd>
                                <dt class="hdlist1">通过 <code>SparkSQL</code> 查询 <code>Hive</code> 的表</dt>
                                <dd>
                                    <div class="sidebarblock">
                                        <div class="content">
                                            <div class="paragraph">
                                                <p>查询 <code>Hive</code> 中的表可以直接通过 <code>spark.sql(&#8230;&#8203;)</code>
                                                    来进行, 可以直接在其中访问 <code>Hive</code> 的 <code>MetaStore</code>, 前提是一定要将
                                                    <code>Hive</code> 的配置文件拷贝到 <code>Spark</code> 的 <code>conf</code> 目录
                                                </p>
                                            </div>
                                            <div class="listingblock">
                                                <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">scala&gt; spark.sql("use spark_integrition")
scala&gt; val resultDF = spark.sql("select * from student limit 10")
scala&gt; resultDF.show()</code></pre>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </dd>
                                <dt class="hdlist1">通过 <code>SparkSQL</code> 创建 <code>Hive</code> 表</dt>
                                <dd>
                                    <div class="sidebarblock">
                                        <div class="content">
                                            <div class="paragraph">
                                                <p>通过 <code>SparkSQL</code> 可以直接创建 <code>Hive</code> 表, 并且使用 <code>LOAD
                                                    DATA</code> 加载数据</p>
                                            </div>
                                            <div class="listingblock">
                                                <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val createTableStr =
  """
    |CREATE EXTERNAL TABLE student
    |(
    |  name  STRING,
    |  age   INT,
    |  gpa   string
    |)
    |ROW FORMAT DELIMITED
    |  FIELDS TERMINATED BY '\t'
    |  LINES TERMINATED BY '\n'
    |STORED AS TEXTFILE
    |LOCATION '/dataset/hive'
  """.stripMargin

spark.sql("CREATE DATABASE IF NOT EXISTS spark_integrition1")
spark.sql("USE spark_integrition1")
spark.sql(createTableStr)
spark.sql("LOAD DATA INPATH '/dataset/studenttab10k' OVERWRITE INTO TABLE student")
spark.sql("select * from student limit").show()</code></pre>
                                                </div>
                                            </div>
                                            <div class="paragraph">
                                                <p>目前 <code>SparkSQL</code> 支持的文件格式有 <code>sequencefile</code>, <code>rcfile</code>,
                                                    <code>orc</code>, <code>parquet</code>, <code>textfile</code>,
                                                    <code>avro</code>, 并且也可以指定 <code>serde</code> 的名称</p>
                                            </div>
                                        </div>
                                    </div>
                                </dd>
                                <dt class="hdlist1">使用 <code>SparkSQL</code> 处理数据并保存进 Hive 表</dt>
                                <dd>
                                    <div class="sidebarblock">
                                        <div class="content">
                                            <div class="paragraph">
                                                <p>前面都在使用 <code>SparkShell</code> 的方式来访问 <code>Hive</code>, 编写
                                                    <code>SQL</code>, 通过 <code>Spark</code> 独立应用的形式也可以做到同样的事,
                                                    但是需要一些前置的步骤, 如下</p>
                                            </div>
                                            <div class="dlist">
                                                <dl>
                                                    <dt class="hdlist1">Step 1: 导入 <code>Maven</code> 依赖</dt>
                                                    <dd>
                                                        <div class="exampleblock">
                                                            <div class="content">
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">&lt;dependency&gt;
    &lt;groupId&gt;org.apache.spark&lt;/groupId&gt;
    &lt;artifactId&gt;spark-hive_2.11&lt;/artifactId&gt;
    &lt;version&gt;${spark.version}&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
                                                                    </div>
                                                                </div>
                                                            </div>
                                                        </div>
                                                    </dd>
                                                    <dt class="hdlist1">Step 2: 配置 <code>SparkSession</code></dt>
                                                    <dd>
                                                        <div class="exampleblock">
                                                            <div class="content">
                                                                <div class="paragraph">
                                                                    <p>如果希望使用 <code>SparkSQL</code> 访问 <code>Hive</code>
                                                                        的话, 需要做两件事</p>
                                                                </div>
                                                                <div class="olist arabic">
                                                                    <ol class="arabic">
                                                                        <li>
                                                                            <p>开启 <code>SparkSession</code> 的
                                                                                <code>Hive</code> 支持</p>
                                                                            <div class="paragraph">
                                                                                <p>经过这一步配置, <code>SparkSQL</code> 才会把
                                                                                    <code>SQL</code> 语句当作
                                                                                    <code>HiveSQL</code> 来进行解析</p>
                                                                            </div>
                                                                        </li>
                                                                        <li>
                                                                            <p>设置 <code>WareHouse</code> 的位置</p>
                                                                            <div class="paragraph">
                                                                                <p>虽然 <code>hive-stie.xml</code> 中已经配置了
                                                                                    <code>WareHouse</code> 的位置, 但是在
                                                                                    <code>Spark 2.0.0</code> 后已经废弃了
                                                                                    <code>hive-site.xml</code> 中设置的
                                                                                    <code>hive.metastore.warehouse.dir</code>,
                                                                                    需要在 <code>SparkSession</code> 中设置
                                                                                    <code>WareHouse</code> 的位置</p>
                                                                            </div>
                                                                        </li>
                                                                        <li>
                                                                            <p>设置 <code>MetaStore</code> 的位置</p>
                                                                        </li>
                                                                    </ol>
                                                                </div>
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = SparkSession
  .builder()
  .appName("hive example")
  .config("spark.sql.warehouse.dir", "hdfs://node01:8020/dataset/hive")  <i class="conum" data-value="1"></i><b>(1)</b>
  .config("hive.metastore.uris", "thrift://node01:9083")                 <i class="conum" data-value="2"></i><b>(2)</b>
  .enableHiveSupport()                                                   <i class="conum" data-value="3"></i><b>(3)</b>
  .getOrCreate()</code></pre>
                                                                    </div>
                                                                </div>
                                                                <div class="colist arabic">
                                                                    <table>
                                                                        <tr>
                                                                            <td><i class="conum"
                                                                                   data-value="1"></i><b>1</b></td>
                                                                            <td>设置 <code>WareHouse</code> 的位置</td>
                                                                        </tr>
                                                                        <tr>
                                                                            <td><i class="conum"
                                                                                   data-value="2"></i><b>2</b></td>
                                                                            <td>设置 <code>MetaStore</code> 的位置</td>
                                                                        </tr>
                                                                        <tr>
                                                                            <td><i class="conum"
                                                                                   data-value="3"></i><b>3</b></td>
                                                                            <td>开启 <code>Hive</code> 支持</td>
                                                                        </tr>
                                                                    </table>
                                                                </div>
                                                            </div>
                                                        </div>
                                                    </dd>
                                                </dl>
                                            </div>
                                            <div class="paragraph">
                                                <p>配置好了以后, 就可以通过 <code>DataFrame</code> 处理数据, 后将数据结果推入 <code>Hive</code>
                                                    表中了, 在将结果保存到 <code>Hive</code> 表的时候, 可以指定保存模式</p>
                                            </div>
                                            <div class="listingblock">
                                                <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val schema = StructType(
  List(
    StructField("name", StringType),
    StructField("age", IntegerType),
    StructField("gpa", FloatType)
  )
)

val studentDF = spark.read
  .option("delimiter", "\t")
  .schema(schema)
  .csv("dataset/studenttab10k")

val resultDF = studentDF.where("age &lt; 50")

resultDF.write.mode(SaveMode.Overwrite).saveAsTable("spark_integrition1.student") <i class="conum"
                                                                                     data-value="1"></i><b>(1)</b></code></pre>
                                                </div>
                                            </div>
                                            <div class="colist arabic">
                                                <table>
                                                    <tr>
                                                        <td><i class="conum" data-value="1"></i><b>1</b></td>
                                                        <td>通过 <code>mode</code> 指定保存模式, 通过 <code>saveAsTable</code>
                                                            保存数据到 <code>Hive</code></td>
                                                    </tr>
                                                </table>
                                            </div>
                                        </div>
                                    </div>
                                </dd>
                            </dl>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_7_6_jdbc">7.6. JDBC</h3>
                    <div class="exampleblock">
                        <div class="title">导读</div>
                        <div class="content">
                            <div class="olist arabic">
                                <ol class="arabic">
                                    <li>
                                        <p>通过 <code>SQL</code> 操作 <code>MySQL</code> 的表</p>
                                    </li>
                                    <li>
                                        <p>将数据写入 <code>MySQL</code> 的表中</p>
                                    </li>
                                </ol>
                            </div>
                        </div>
                    </div>
                    <div class="dlist">
                        <dl>
                            <dt class="hdlist1">准备 <code>MySQL</code> 环境</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>在使用 <code>SparkSQL</code> 访问 <code>MySQL</code> 之前, 要对 <code>MySQL</code>
                                                进行一些操作, 例如说创建用户, 表和库等</p>
                                        </div>
                                        <div class="ulist">
                                            <ul>
                                                <li>
                                                    <p>Step 1: 连接 <code>MySQL</code> 数据库</p>
                                                    <div class="openblock">
                                                        <div class="content">
                                                            <div class="paragraph">
                                                                <p>在 <code>MySQL</code> 所在的主机上执行如下命令</p>
                                                            </div>
                                                            <div class="listingblock">
                                                                <div class="content">
                                                                    <pre class="highlightjs highlight"><code
                                                                            class="language-text hljs" data-lang="text">mysql -u root -p</code></pre>
                                                                </div>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </li>
                                                <li>
                                                    <p>Step 2: 创建 <code>Spark</code> 使用的用户</p>
                                                    <div class="openblock">
                                                        <div class="content">
                                                            <div class="paragraph">
                                                                <p>登进 <code>MySQL</code> 后, 需要先创建用户</p>
                                                            </div>
                                                            <div class="listingblock">
                                                                <div class="content">
<pre class="highlightjs highlight"><code class="language-sql hljs" data-lang="sql">CREATE USER 'spark'@'%' IDENTIFIED BY 'Spark123!';
GRANT ALL ON spark_test.* TO 'spark'@'%';</code></pre>
                                                                </div>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </li>
                                                <li>
                                                    <p>Step 3: 创建库和表</p>
                                                    <div class="openblock">
                                                        <div class="content">
                                                            <div class="listingblock">
                                                                <div class="content">
<pre class="highlightjs highlight"><code class="language-sql hljs" data-lang="sql">CREATE DATABASE spark_test;

USE spark_test;

CREATE TABLE IF NOT EXISTS `student`(
`id` INT AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`age` INT NOT NULL,
`gpa` FLOAT,
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;</code></pre>
                                                                </div>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">使用 <code>SparkSQL</code> 向 <code>MySQL</code> 中写入数据</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>其实在使用 <code>SparkSQL</code> 访问 <code>MySQL</code> 是通过 <code>JDBC</code>,
                                                那么其实所有支持 <code>JDBC</code> 的数据库理论上都可以通过这种方式进行访问</p>
                                        </div>
                                        <div class="paragraph">
                                            <p>在使用 <code>JDBC</code> 访问关系型数据的时候, 其实也是使用 <code>DataFrameReader</code>, 对
                                                <code>DataFrameReader</code> 提供一些配置, 就可以使用 <code>Spark</code> 访问 <code>JDBC</code>,
                                                有如下几个配置可用</p>
                                        </div>
                                        <table class="tableblock frame-all grid-all stretch">
                                            <colgroup>
                                                <col style="width: 50%;">
                                                <col style="width: 50%;">
                                            </colgroup>
                                            <thead>
                                            <tr>
                                                <th class="tableblock halign-left valign-top">属性</th>
                                                <th class="tableblock halign-left valign-top">含义</th>
                                            </tr>
                                            </thead>
                                            <tbody>
                                            <tr>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    <code>url</code></p></td>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">要连接的
                                                    <code>JDBC URL</code></p></td>
                                            </tr>
                                            <tr>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    <code>dbtable</code></p></td>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    要访问的表, 可以使用任何 <code>SQL</code> 语句中 <code>from</code> 子句支持的语法</p>
                                                </td>
                                            </tr>
                                            <tr>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    <code>fetchsize</code></p></td>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    数据抓取的大小(单位行), 适用于读的情况</p></td>
                                            </tr>
                                            <tr>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    <code>batchsize</code></p></td>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    数据传输的大小(单位行), 适用于写的情况</p></td>
                                            </tr>
                                            <tr>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    <code>isolationLevel</code></p></td>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    事务隔离级别, 是一个枚举, 取值 <code>NONE</code>, <code>READ_COMMITTED</code>,
                                                    <code>READ_UNCOMMITTED</code>, <code>REPEATABLE_READ</code>, <code>SERIALIZABLE</code>,
                                                    默认为 <code>READ_UNCOMMITTED</code></p></td>
                                            </tr>
                                            </tbody>
                                        </table>
                                        <div class="paragraph">
                                            <p>读取数据集, 处理过后存往 <code>MySQL</code> 中的代码如下</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = SparkSession
  .builder()
  .appName("hive example")
  .master("local[6]")
  .getOrCreate()

val schema = StructType(
  List(
    StructField("name", StringType),
    StructField("age", IntegerType),
    StructField("gpa", FloatType)
  )
)

val studentDF = spark.read
  .option("delimiter", "\t")
  .schema(schema)
  .csv("dataset/studenttab10k")

studentDF.write.format("jdbc").mode(SaveMode.Overwrite)
  .option("url", "jdbc:mysql://node01:3306/spark_test")
  .option("dbtable", "student")
  .option("user", "spark")
  .option("password", "Spark123!")
  .save()</code></pre>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">运行程序</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>如果是在本地运行, 需要导入 <code>Maven</code> 依赖</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;dependency&gt;
    &lt;groupId&gt;mysql&lt;/groupId&gt;
    &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
    &lt;version&gt;5.1.47&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p>如果使用 <code>Spark submit</code> 或者 <code>Spark shell</code> 来运行任务, 需要通过
                                                <code>--jars</code> 参数提交 <code>MySQL</code> 的 <code>Jar</code> 包, 或者指定
                                                <code>--packages</code> 从 <code>Maven</code> 库中读取</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
                                                <pre class="highlightjs highlight"><code class="language-text hljs"
                                                                                         data-lang="text">bin/spark-shell --packages  mysql:mysql-connector-java:5.1.47 --repositories http://maven.aliyun.com/nexus/content/groups/public/</code></pre>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                            <dt class="hdlist1">从 <code>MySQL</code> 中读取数据</dt>
                            <dd>
                                <div class="sidebarblock">
                                    <div class="content">
                                        <div class="paragraph">
                                            <p>读取 <code>MySQL</code> 的方式也非常的简单, 只是使用 <code>SparkSQL</code> 的 <code>DataFrameReader</code>
                                                加上参数配置即可访问</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">spark.read.format("jdbc")
  .option("url", "jdbc:mysql://node01:3306/spark_test")
  .option("dbtable", "student")
  .option("user", "spark")
  .option("password", "Spark123!")
  .load()
  .show()</code></pre>
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p>默认情况下读取 <code>MySQL</code> 表时, 从 <code>MySQL</code> 表中读取的数据放入了一个分区,
                                                拉取后可以使用 <code>DataFrame</code> 重分区来保证并行计算和内存占用不会太高, 但是如果感觉
                                                <code>MySQL</code> 中数据过多的时候, 读取时可能就会产生 <code>OOM</code>, 所以在数据量比较大的场景,
                                                就需要在读取的时候就将其分发到不同的 <code>RDD</code> 分区</p>
                                        </div>
                                        <table class="tableblock frame-all grid-all stretch">
                                            <colgroup>
                                                <col style="width: 50%;">
                                                <col style="width: 50%;">
                                            </colgroup>
                                            <thead>
                                            <tr>
                                                <th class="tableblock halign-left valign-top">属性</th>
                                                <th class="tableblock halign-left valign-top">含义</th>
                                            </tr>
                                            </thead>
                                            <tbody>
                                            <tr>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    <code>partitionColumn</code></p></td>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    指定按照哪一列进行分区, 只能设置类型为数字的列, 一般指定为 <code>ID</code></p></td>
                                            </tr>
                                            <tr>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    <code>lowerBound</code>, <code>upperBound</code></p></td>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    确定步长的参数, <code>lowerBound - upperBound</code> 之间的数据均分给每一个分区, 小于
                                                    <code>lowerBound</code> 的数据分给第一个分区, 大于 <code>upperBound</code>
                                                    的数据分给最后一个分区</p></td>
                                            </tr>
                                            <tr>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    <code>numPartitions</code></p></td>
                                                <td class="tableblock halign-left valign-top"><p class="tableblock">
                                                    分区数量</p></td>
                                            </tr>
                                            </tbody>
                                        </table>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">spark.read.format("jdbc")
  .option("url", "jdbc:mysql://node01:3306/spark_test")
  .option("dbtable", "student")
  .option("user", "spark")
  .option("password", "Spark123!")
  .option("partitionColumn", "age")
  .option("lowerBound", 1)
  .option("upperBound", 60)
  .option("numPartitions", 10)
  .load()
  .show()</code></pre>
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p>有时候可能要使用非数字列来作为分区依据, <code>Spark</code> 也提供了针对任意类型的列作为分区依据的方法</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val predicates = Array(
  "age &lt; 20",
  "age &gt;= 20, age &lt; 30",
  "age &gt;= 30"
)

val connectionProperties = new Properties()
connectionProperties.setProperty("user", "spark")
connectionProperties.setProperty("password", "Spark123!")

spark.read
  .jdbc(
    url = "jdbc:mysql://node01:3306/spark_test",
    table = "student",
    predicates = predicates,
    connectionProperties = connectionProperties
  )
  .show()</code></pre>
                                            </div>
                                        </div>
                                        <div class="paragraph">
                                            <p><code>SparkSQL</code> 中并没有直接提供按照 <code>SQL</code> 进行筛选读取数据的
                                                <code>API</code> 和参数, 但是可以通过 <code>dbtable</code> 来曲线救国,
                                                <code>dbtable</code> 指定目标表的名称, 但是因为 <code>dbtable</code> 中可以编写
                                                <code>SQL</code>, 所以使用子查询即可做到</p>
                                        </div>
                                        <div class="listingblock">
                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">spark.read.format("jdbc")
  .option("url", "jdbc:mysql://node01:3306/spark_test")
  .option("dbtable", "(select name, age from student where age &gt; 10 and age &lt; 20) as stu")
  .option("user", "spark")
  .option("password", "Spark123!")
  .option("partitionColumn", "age")
  .option("lowerBound", 1)
  .option("upperBound", 60)
  .option("numPartitions", 10)
  .load()
  .show()</code></pre>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </dd>
                        </dl>
                    </div>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_8_dataset_dataframe_的基础操作">8. Dataset (DataFrame) 的基础操作</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">导读</div>
                    <div class="content">
                        <div class="paragraph">
                            <p>这一章节主要目的是介绍 <code>Dataset</code> 的基础操作, 当然, <code>DataFrame</code> 就是
                                <code>Dataset</code>, 所以这些操作大部分也适用于 <code>DataFrame</code></p>
                        </div>
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>有类型的转换操作</p>
                                </li>
                                <li>
                                    <p>无类型的转换操作</p>
                                </li>
                                <li>
                                    <p>基础 <code>Action</code></p>
                                </li>
                                <li>
                                    <p>空值如何处理</p>
                                </li>
                                <li>
                                    <p>统计操作</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="sect2">
                    <h3 id="_8_1_有类型操作">8.1. 有类型操作</h3>
                    <table class="tableblock frame-all grid-all stretch">
                        <colgroup>
                            <col style="width: 15%;">
                            <col style="width: 15%;">
                            <col>
                        </colgroup>
                        <thead>
                        <tr>
                            <th class="tableblock halign-left valign-top">分类</th>
                            <th class="tableblock halign-left valign-top">算子</th>
                            <th class="tableblock halign-left valign-top">解释</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="5"><p class="tableblock">转换</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>flatMap</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>通过 <code>flatMap</code> 可以将一条数据转为一个数组, 后再展开这个数组放入 <code>Dataset</code></p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq("hello world", "hello pc").toDS()
ds.flatMap( _.split(" ") ).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>map</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>map</code> 可以将数据集中每条数据转为另一种形式</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 15), Person("lisi", 15)).toDS()
ds.map( person =&gt; Person(person.name, person.age * 2) ).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>mapPartitions</code></p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>mapPartitions</code> 和 <code>map</code> 一样, 但是 <code>map</code>
                                            的处理单位是每条数据, <code>mapPartitions</code> 的处理单位是每个分区</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 15), Person("lisi", 15)).toDS()
ds.mapPartitions( iter =&gt; {
    val returnValue = iter.map(
      item =&gt; Person(item.name, item.age * 2)
    )
    returnValue
  } )
  .show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>transform</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>map</code> 和 <code>mapPartitions</code> 以及 <code>transform</code> 都是转换,
                                            <code>map</code> 和 <code>mapPartitions</code> 是针对数据, 而
                                            <code>transform</code> 是针对整个数据集, 这种方式最大的区别就是 <code>transform</code> 可以直接拿到
                                            <code>Dataset</code> 进行操作</p>
                                    </div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="20190526111401"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190526111401.png" width="600">
                                        </div>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = spark.range(5)
ds.transform( dataset =&gt; dataset.withColumn("doubled", 'id * 2) )</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>as</code></p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>as[Type]</code> 算子的主要作用是将弱类型的 <code>Dataset</code> 转为强类型的
                                            <code>Dataset</code>, 它有很多适用场景, 但是最常见的还是在读取数据的时候, 因为
                                            <code>DataFrameReader</code> 体系大部分情况下是将读出来的数据转换为 <code>DataFrame</code> 的形式,
                                            如果后续需要使用 <code>Dataset</code> 的强类型 <code>API</code>, 则需要将
                                            <code>DataFrame</code> 转为 <code>Dataset</code>. 可以使用 <code>as[Type]</code>
                                            算子完成这种操作</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._

val structType = StructType(
  Seq(
    StructField("name", StringType),
    StructField("age", IntegerType),
    StructField("gpa", FloatType)
  )
)

val sourceDF = spark.read
  .schema(structType)
  .option("delimiter", "\t")
  .csv("dataset/studenttab10k")

val dataset = sourceDF.as[Student]
dataset.show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">过滤</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>filter</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>filter</code> 用来按照条件过滤数据集</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 15), Person("lisi", 15)).toDS()
ds.filter( person =&gt; person.name == "lisi" ).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">聚合</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>groupByKey</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>grouByKey</code> 算子的返回结果是 <code>KeyValueGroupedDataset</code>, 而不是一个
                                            <code>Dataset</code>, 所以必须要先经过 <code>KeyValueGroupedDataset</code> 中的方法进行聚合,
                                            再转回 <code>Dataset</code>, 才能使用 <code>Action</code> 得出结果</p>
                                    </div>
                                    <div class="paragraph">
                                        <p>其实这也印证了分组后必须聚合的道理</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 15), Person("zhangsan", 15), Person("lisi", 15)).toDS()
ds.groupByKey( person =&gt; person.name ).count().show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="2"><p class="tableblock">切分</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>randomSplit</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>randomSplit</code> 会按照传入的权重随机将一个 <code>Dataset</code> 分为多个 <code>Dataset</code>,
                                            传入 <code>randomSplit</code> 的数组有多少个权重, 最终就会生成多少个 <code>Dataset</code>,
                                            这些权重的加倍和应该为 1, 否则将被标准化</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds = spark.range(15)
val datasets: Array[Dataset[lang.Long]] = ds.randomSplit(Array[Double](2, 3))
datasets.foreach(dataset =&gt; dataset.show())</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>sample</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>sample</code> 会随机在 <code>Dataset</code> 中抽样</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds = spark.range(15)
ds.sample(withReplacement = false, fraction = 0.4).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="2"><p class="tableblock">排序</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>orderBy</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>orderBy</code> 配合 <code>Column</code> 的 <code>API</code>, 可以实现正反序排列</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.orderBy("age").show()
ds.orderBy('age.desc).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>sort</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>其实 <code>orderBy</code> 是 <code>sort</code> 的别名, 所以它们所实现的功能是一样的</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.sort('age.desc).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="2"><p class="tableblock">分区</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>coalesce</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>减少分区, 此算子和 <code>RDD</code> 中的 <code>coalesce</code> 不同, <code>Dataset</code>
                                            中的 <code>coalesce</code> 只能减少分区数, <code>coalesce</code> 会直接创建一个逻辑操作, 并且设置
                                            <code>Shuffle</code> 为 <code>false</code></p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds = spark.range(15)
ds.coalesce(1).explain(true)</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>repartitions</code></p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>repartitions</code> 有两个作用, 一个是重分区到特定的分区数, 另一个是按照某一列来分区, 类似于
                                            <code>SQL</code> 中的 <code>DISTRIBUTE BY</code></p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.repartition(4)
ds.repartition('name)</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="2"><p class="tableblock">去重</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>dropDuplicates</code></p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>使用 <code>dropDuplicates</code> 可以去掉某一些列中重复的行</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = spark.createDataset(Seq(Person("zhangsan", 15), Person("zhangsan", 15), Person("lisi", 15)))
ds.dropDuplicates("age").show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>distinct</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>当 <code>dropDuplicates</code> 中没有传入列名的时候, 其含义是根据所有列去重,
                                            <code>dropDuplicates()</code> 方法还有一个别名, 叫做 <code>distinct</code></p>
                                    </div>
                                    <div class="imageblock">
                                        <div class="content">
                                            <img alt="20190525182912"
                                                 src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190525182912.png" width="800">
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>所以, 使用 <code>distinct</code> 也可以去重, 并且只能根据所有的列来去重</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = spark.createDataset(Seq(Person("zhangsan", 15), Person("zhangsan", 15), Person("lisi", 15)))
ds.distinct().show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="4"><p class="tableblock">集合操作</p>
                            </th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>except</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>except</code> 和 <code>SQL</code> 语句中的 <code>except</code> 一个意思, 是求得
                                            <code>ds1</code> 中不存在于 <code>ds2</code> 中的数据, 其实就是差集</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds1 = spark.range(1, 10)
val ds2 = spark.range(5, 15)

ds1.except(ds2).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>intersect</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>求得两个集合的交集</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds1 = spark.range(1, 10)
val ds2 = spark.range(5, 15)

ds1.intersect(ds2).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>union</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>求得两个集合的并集</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds1 = spark.range(1, 10)
val ds2 = spark.range(5, 15)

ds1.union(ds2).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>limit</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>限制结果集数量</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val ds = spark.range(1, 10)
ds.limit(3).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
                <div class="sect2">
                    <h3 id="_8_2_无类型转换">8.2. 无类型转换</h3>
                    <table class="tableblock frame-all grid-all stretch">
                        <colgroup>
                            <col style="width: 15%;">
                            <col style="width: 15%;">
                            <col>
                        </colgroup>
                        <thead>
                        <tr>
                            <th class="tableblock halign-left valign-top">分类</th>
                            <th class="tableblock halign-left valign-top">算子</th>
                            <th class="tableblock halign-left valign-top">解释</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="4"><p class="tableblock">选择</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>select</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>select</code> 用来选择某些列出现在结果集中</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.select($"name").show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>selectExpr</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>在 <code>SQL</code> 语句中, 经常可以在 <code>select</code> 子句中使用
                                            <code>count(age)</code>, <code>rand()</code> 等函数, 在 <code>selectExpr</code>
                                            中就可以使用这样的 <code>SQL</code> 表达式, 同时使用 <code>select</code> 配合
                                            <code>expr</code> 函数也可以做到类似的效果</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
import org.apache.spark.sql.functions._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.selectExpr("count(age) as count").show()
ds.selectExpr("rand() as random").show()
ds.select(expr("count(age) as count")).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>withColumn</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>通过 <code>Column</code> 对象在 <code>Dataset</code> 中创建一个新的列或者修改原来的列</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
import org.apache.spark.sql.functions._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.withColumn("random", expr("rand()")).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>withColumnRenamed</code></p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>修改列名</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.withColumnRenamed("name", "new_name").show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">剪除</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">drop</p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>剪掉某个列</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.drop('age).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">聚合</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">groupBy</p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>按照给定的行进行分组</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import spark.implicits._
val ds = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()
ds.groupBy('name).count().show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
                <div class="sect2">
                    <h3 id="_8_5_column_对象">8.5. Column 对象</h3>
                    <div class="exampleblock">
                        <div class="title">导读</div>
                        <div class="content">
                            <div class="paragraph">
                                <p>Column 表示了 Dataset 中的一个列, 并且可以持有一个表达式, 这个表达式作用于每一条数据, 对每条数据都生成一个值,
                                    之所以有单独这样的一个章节是因为列的操作属于细节, 但是又比较常见, 会在很多算子中配合出现</p>
                            </div>
                        </div>
                    </div>
                    <table class="tableblock frame-all grid-all stretch">
                        <colgroup>
                            <col style="width: 15%;">
                            <col style="width: 15%;">
                            <col>
                        </colgroup>
                        <thead>
                        <tr>
                            <th class="tableblock halign-left valign-top">分类</th>
                            <th class="tableblock halign-left valign-top">操作</th>
                            <th class="tableblock halign-left valign-top">解释</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="6"><p class="tableblock">创建</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>'</code></p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>单引号 <code>'</code> 在 Scala 中是一个特殊的符号, 通过 <code>'</code> 会生成一个
                                            <code>Symbol</code> 对象, <code>Symbol</code> 对象可以理解为是一个字符串的变种, 但是比字符串的效率高很多,
                                            在 <code>Spark</code> 中, 对 <code>Scala</code> 中的 <code>Symbol</code>
                                            对象做了隐式转换, 转换为一个 <code>ColumnName</code> 对象, <code>ColumnName</code> 是 <code>Column</code>
                                            的子类, 所以在 <code>Spark</code> 中可以如下去选中一个列</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = SparkSession.builder().appName("column").master("local[6]").getOrCreate()
import spark.implicits._
val personDF = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()

val c1: Symbol = 'name</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>$</code></p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>同理, <code>$</code> 符号也是一个隐式转换, 同样通过 <code>spark.implicits</code> 导入, 通过
                                            <code>$</code> 可以生成一个 <code>Column</code> 对象</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = SparkSession.builder().appName("column").master("local[6]").getOrCreate()
import spark.implicits._
val personDF = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()

val c2: ColumnName = $"name"</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>col</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>SparkSQL</code> 提供了一系列的函数, 可以通过函数实现很多功能, 在后面课程中会进行详细介绍,
                                            这些函数中有两个可以帮助我们创建 <code>Column</code> 对象, 一个是 <code>col</code>, 另外一个是 <code>column</code>
                                        </p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = SparkSession.builder().appName("column").master("local[6]").getOrCreate()
import org.apache.spark.sql.functions._
val personDF = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()

val c3: sql.Column = col("name")</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>column</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = SparkSession.builder().appName("column").master("local[6]").getOrCreate()
import org.apache.spark.sql.functions._
val personDF = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()

val c4: sql.Column = column("name")</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>Dataset.col</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>前面的 <code>Column</code> 对象创建方式所创建的 <code>Column</code> 对象都是 <code>Free</code>
                                            的, 也就是没有绑定任何 <code>Dataset</code>, 所以可以作用于任何 <code>Dataset</code>, 同时, 也可以通过
                                            <code>Dataset</code> 的 <code>col</code> 方法选择一个列, 但是这个 <code>Column</code>
                                            是绑定了这个 <code>Dataset</code> 的, 所以只能用于创建其的 <code>Dataset</code> 上</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = SparkSession.builder().appName("column").master("local[6]").getOrCreate()
val personDF = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()

val c5: sql.Column = personDF.col("name")</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">
                                <code>Dataset.apply</code></p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>可以通过 <code>Dataset</code> 对象的 <code>apply</code> 方法来获取一个关联此
                                            <code>Dataset</code> 的 <code>Column</code> 对象</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val spark = SparkSession.builder().appName("column").master("local[6]").getOrCreate()
val personDF = Seq(Person("zhangsan", 12), Person("zhangsan", 8), Person("lisi", 15)).toDS()

val c6: sql.Column = personDF.apply("name")</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p><code>apply</code> 的调用有一个简写形式</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
                                            <pre class="highlightjs highlight"><code class="language-scala hljs"
                                                                                     data-lang="scala">val c7: sql.Column = personDF("name")</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="2"><p class="tableblock">别名和转换</p>
                            </th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>as[Type]</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>as</code> 方法有两个用法, 通过 <code>as[Type]</code> 的形式可以将一个列中数据的类型转为 <code>Type</code>
                                            类型</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
                                            <pre class="highlightjs highlight"><code class="language-scala hljs"
                                                                                     data-lang="scala">personDF.select(col("age").as[Long]).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>as(name)</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>通过 <code>as(name)</code> 的形式使用 <code>as</code> 方法可以为列创建别名</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
                                            <pre class="highlightjs highlight"><code class="language-scala hljs"
                                                                                     data-lang="scala">personDF.select(col("age").as("age_new")).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock">添加列</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>withColumn</code>
                            </p></th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>通过 <code>Column</code> 在添加一个新的列时候修改 <code>Column</code> 所代表的列的数据</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
                                            <pre class="highlightjs highlight"><code class="language-scala hljs"
                                                                                     data-lang="scala">personDF.withColumn("double_age", 'age * 2).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top" rowspan="3"><p class="tableblock">操作</p></th>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>like</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>通过 <code>Column</code> 的 <code>API</code>, 可以轻松实现 <code>SQL</code> 语句中 <code>LIKE</code>
                                            的功能</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
                                            <pre class="highlightjs highlight"><code class="language-scala hljs"
                                                                                     data-lang="scala">personDF.filter('name like "%zhang%").show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>isin</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>通过 <code>Column</code> 的 <code>API</code>, 可以轻松实现 <code>SQL</code> 语句中 <code>ISIN</code>
                                            的功能</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
                                            <pre class="highlightjs highlight"><code class="language-scala hljs"
                                                                                     data-lang="scala">personDF.filter('name isin ("hello", "zhangsan")).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>sort</code></p>
                            </th>
                            <td class="tableblock halign-left valign-top">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>在排序的时候, 可以通过 <code>Column</code> 的 <code>API</code> 实现正反序</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">personDF.sort('age.asc).show()
personDF.sort('age.desc).show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_9_缺失值处理">9. 缺失值处理</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">导读</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p><code>DataFrame</code> 中什么时候会有无效值</p>
                                </li>
                                <li>
                                    <p><code>DataFrame</code> 如何处理无效的值</p>
                                </li>
                                <li>
                                    <p><code>DataFrame</code> 如何处理 <code>null</code></p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="dlist">
                    <dl>
                        <dt class="hdlist1">缺失值的处理思路</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>如果想探究如何处理无效值, 首先要知道无效值从哪来, 从而分析可能产生的无效值有哪些类型, 在分别去看如何处理无效值</p>
                                    </div>
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">什么是缺失值</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>一个值本身的含义是这个值不存在则称之为缺失值, 也就是说这个值本身代表着缺失, 或者这个值本身无意义, 比如说
                                                                <code>null</code>, 比如说空字符串</p>
                                                        </div>
                                                        <div class="imageblock">
                                                            <div class="content">
                                                                <img alt="20190527220736"
                                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190527220736.png">
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>关于数据的分析其实就是统计分析的概念, 如果这样的话, 当数据集中存在缺失值, 则无法进行统计和分析,
                                                                对很多操作都有影响</p>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">缺失值如何产生的</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="imageblock">
                                                            <div class="content">
                                                                <img alt="20190527215718"
                                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190527215718.png">
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>Spark 大多时候处理的数据来自于业务系统中, 业务系统中可能会因为各种原因, 产生一些异常的数据</p>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>例如说因为前后端的判断失误, 提交了一些非法参数. 再例如说因为业务系统修改 <code>MySQL</code>
                                                                表结构产生的一些空值数据等. 总之在业务系统中出现缺失值其实是非常常见的一件事,
                                                                所以大数据系统就一定要考虑这件事.</p>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">缺失值的类型</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>常见的缺失值有两种</p>
                                                        </div>
                                                        <div class="ulist">
                                                            <ul>
                                                                <li>
                                                                    <p><code>null</code>, <code>NaN</code> 等特殊类型的值,
                                                                        某些语言中 <code>null</code> 可以理解是一个对象, 但是代表没有对象,
                                                                        <code>NaN</code> 是一个数字, 可以代表不是数字</p>
                                                                    <div class="openblock">
                                                                        <div class="content">
                                                                            <div class="paragraph">
                                                                                <p>针对这一类的缺失值, <code>Spark</code> 提供了一个名为
                                                                                    <code>DataFrameNaFunctions</code>
                                                                                    特殊类型来操作和处理</p>
                                                                            </div>
                                                                        </div>
                                                                    </div>
                                                                </li>
                                                                <li>
                                                                    <p><code>"Null"</code>, <code>"NA"</code>, <code>"
                                                                        "</code> 等解析为字符串的类型, 但是其实并不是常规字符串数据</p>
                                                                    <div class="openblock">
                                                                        <div class="content">
                                                                            <div class="paragraph">
                                                                                <p>针对这类字符串, 需要对数据集进行采样, 观察异常数据, 总结经验,
                                                                                    各个击破</p>
                                                                            </div>
                                                                        </div>
                                                                    </div>
                                                                </li>
                                                            </ul>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>DataFrameNaFunctions</code></dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>DataFrameNaFunctions</code> 使用 <code>Dataset</code> 的 <code>na</code>
                                            函数来获取</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val df = ...
val naFunc: DataFrameNaFunctions = df.na</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>当数据集中出现缺失值的时候, 大致有两种处理方式, 一个是丢弃, 一个是替换为某值, <code>DataFrameNaFunctions</code>
                                            中包含一系列针对空值数据的方案</p>
                                    </div>
                                    <div class="ulist">
                                        <ul>
                                            <li>
                                                <p><code>DataFrameNaFunctions.drop</code> 可以在当某行中包含 <code>null</code> 或
                                                    <code>NaN</code> 的时候丢弃此行</p>
                                            </li>
                                            <li>
                                                <p><code>DataFrameNaFunctions.fill</code> 可以在将 <code>null</code> 和
                                                    <code>NaN</code> 充为其它值</p>
                                            </li>
                                            <li>
                                                <p><code>DataFrameNaFunctions.replace</code> 可以把 <code>null</code> 或
                                                    <code>NaN</code> 替换为其它值, 但是和 <code>fill</code> 略有一些不同, 这个方法针对值来进行替换
                                                </p>
                                            </li>
                                        </ul>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">如何使用 <code>SparkSQL</code> 处理 <code>null</code> 和 <code>NaN</code> ?</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>首先要将数据读取出来, 此次使用的数据集直接存在 <code>NaN</code>, 在指定 <code>Schema</code> 后, 可直接被转为
                                            <code>Double.NaN</code></p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val schema = StructType(
  List(
    StructField("id", IntegerType),
    StructField("year", IntegerType),
    StructField("month", IntegerType),
    StructField("day", IntegerType),
    StructField("hour", IntegerType),
    StructField("season", IntegerType),
    StructField("pm", DoubleType)
  )
)

val df = spark.read
  .option("header", value = true)
  .schema(schema)
  .csv("dataset/beijingpm_with_nan.csv")</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>对于缺失值的处理一般就是丢弃和填充</p>
                                    </div>
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">丢弃包含 <code>null</code> 和 <code>NaN</code> 的行</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>当某行数据所有值都是 <code>null</code> 或者 <code>NaN</code> 的时候丢弃此行
                                                            </p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
                                                                <pre class="highlightjs highlight"><code
                                                                        class="language-scala hljs" data-lang="scala">df.na.drop("all").show()</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>当某行中特定列所有值都是 <code>null</code> 或者 <code>NaN</code>
                                                                的时候丢弃此行</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
                                                                <pre class="highlightjs highlight"><code
                                                                        class="language-scala hljs" data-lang="scala">df.na.drop("all", List("pm", "id")).show()</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>当某行数据任意一个字段为 <code>null</code> 或者 <code>NaN</code>
                                                                的时候丢弃此行</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">df.na.drop().show()
df.na.drop("any").show()</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>当某行中特定列任意一个字段为 <code>null</code> 或者 <code>NaN</code>
                                                                的时候丢弃此行</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">df.na.drop(List("pm", "id")).show()
df.na.drop("any", List("pm", "id")).show()</code></pre>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">填充包含 <code>null</code> 和 <code>NaN</code> 的列</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>填充所有包含 <code>null</code> 和 <code>NaN</code> 的列</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
                                                                <pre class="highlightjs highlight"><code
                                                                        class="language-scala hljs" data-lang="scala">df.na.fill(0).show()</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>填充特定包含 <code>null</code> 和 <code>NaN</code> 的列</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
                                                                <pre class="highlightjs highlight"><code
                                                                        class="language-scala hljs" data-lang="scala">df.na.fill(0, List("pm")).show()</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>根据包含 <code>null</code> 和 <code>NaN</code> 的列的不同来填充</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import scala.collection.JavaConverters._

df.na.fill(Map[String, Any]("pm" -&gt; 0).asJava).show</code></pre>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">如何使用 <code>SparkSQL</code> 处理异常字符串 ?</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>读取数据集, 这次读取的是最原始的那个 <code>PM</code> 数据集</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val df = spark.read
  .option("header", value = true)
  .csv("dataset/BeijingPM20100101_20151231.csv")</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>使用函数直接转换非法的字符串</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">df.select('No as "id", 'year, 'month, 'day, 'hour, 'season,
    when('PM_Dongsi === "NA", 0)
    .otherwise('PM_Dongsi cast DoubleType)
    .as("pm"))
  .show()</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>使用 <code>where</code> 直接过滤</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">df.select('No as "id", 'year, 'month, 'day, 'hour, 'season, 'PM_Dongsi)
  .where('PM_Dongsi =!= "NA")
  .show()</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>使用 <code>DataFrameNaFunctions</code> 替换, 但是这种方式被替换的值和新值必须是同类型</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">df.select('No as "id", 'year, 'month, 'day, 'hour, 'season, 'PM_Dongsi)
  .na.replace("PM_Dongsi", Map("NA" -&gt; "NaN"))
  .show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                    </dl>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_10_聚合">10. 聚合</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">导读</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p><code>groupBy</code></p>
                                </li>
                                <li>
                                    <p><code>rollup</code></p>
                                </li>
                                <li>
                                    <p><code>cube</code></p>
                                </li>
                                <li>
                                    <p><code>pivot</code></p>
                                </li>
                                <li>
                                    <p><code>RelationalGroupedDataset</code> 上的聚合操作</p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="dlist">
                    <dl>
                        <dt class="hdlist1"><code>groupBy</code></dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>groupBy</code> 算子会按照列将 <code>Dataset</code> 分组, 并返回一个 <code>RelationalGroupedDataset</code>
                                            对象, 通过 <code>RelationalGroupedDataset</code> 可以对分组进行聚合</p>
                                    </div>
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">Step 1: 加载实验数据</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">private val spark = SparkSession.builder()
    .master("local[6]")
    .appName("aggregation")
    .getOrCreate()

  import spark.implicits._

  private val schema = StructType(
    List(
      StructField("id", IntegerType),
      StructField("year", IntegerType),
      StructField("month", IntegerType),
      StructField("day", IntegerType),
      StructField("hour", IntegerType),
      StructField("season", IntegerType),
      StructField("pm", DoubleType)
    )
  )

  private val pmDF = spark.read
    .schema(schema)
    .option("header", value = true)
    .csv("dataset/pm_without_null.csv")</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 2: 使用 <code>functions</code> 函数进行聚合</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import org.apache.spark.sql.functions._

val groupedDF: RelationalGroupedDataset = pmDF.groupBy('year)

groupedDF.agg(avg('pm) as "pm_avg")
  .orderBy('pm_avg)
  .show()</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 3: 除了使用 <code>functions</code> 进行聚合, 还可以直接使用 <code>RelationalGroupedDataset</code>
                                                的 <code>API</code> 进行聚合
                                            </dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">groupedDF.avg("pm")
  .orderBy('pm_avg)
  .show()

groupedDF.max("pm")
  .orderBy('pm_avg)
  .show()</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">多维聚合</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>我们可能经常需要针对数据进行多维的聚合, 也就是一次性统计小计, 总计等, 一般的思路如下</p>
                                    </div>
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">Step 1: 准备数据</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">private val spark = SparkSession.builder()
  .master("local[6]")
  .appName("aggregation")
  .getOrCreate()

import spark.implicits._

private val schemaFinal = StructType(
  List(
    StructField("source", StringType),
    StructField("year", IntegerType),
    StructField("month", IntegerType),
    StructField("day", IntegerType),
    StructField("hour", IntegerType),
    StructField("season", IntegerType),
    StructField("pm", DoubleType)
  )
)

private val pmFinal = spark.read
  .schema(schemaFinal)
  .option("header", value = true)
  .csv("dataset/pm_final.csv")</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 2: 进行多维度聚合</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import org.apache.spark.sql.functions._

val groupPostAndYear = pmFinal.groupBy('source, 'year)
  .agg(sum("pm") as "pm")

val groupPost = pmFinal.groupBy('source)
  .agg(sum("pm") as "pm")
  .select('source, lit(null) as "year", 'pm)

groupPostAndYear.union(groupPost)
  .sort('source, 'year asc_nulls_last, 'pm)
  .show()</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                    <div class="paragraph">
                                        <p>大家其实也能看出来, 在一个数据集中又小计又总计, 可能需要多个操作符, 如何简化呢? 请看下面</p>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>rollup</code> 操作符</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>rollup</code> 操作符其实就是 <code>groupBy</code> 的一个扩展, <code>rollup</code>
                                            会对传入的列进行滚动 <code>groupBy</code>, <code>groupBy</code> 的次数为列数量 <code>+
                                                1</code>, 最后一次是对整个数据集进行聚合</p>
                                    </div>
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">Step 1: 创建数据集</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import org.apache.spark.sql.functions._

val sales = Seq(
  ("Beijing", 2016, 100),
  ("Beijing", 2017, 200),
  ("Shanghai", 2015, 50),
  ("Shanghai", 2016, 150),
  ("Guangzhou", 2017, 50)
).toDF("city", "year", "amount")</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 1: <code>rollup</code> 的操作</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">sales.rollup("city", "year")
  .agg(sum("amount") as "amount")
  .sort($"city".desc_nulls_last, $"year".asc_nulls_last)
  .show()

/**
  * 结果集:
  * +---------+----+------+
  * |     city|year|amount|
  * +---------+----+------+
  * | Shanghai|2015|    50| &lt;-- 上海 2015 的小计
  * | Shanghai|2016|   150|
  * | Shanghai|null|   200| &lt;-- 上海的总计
  * |Guangzhou|2017|    50|
  * |Guangzhou|null|    50|
  * |  Beijing|2016|   100|
  * |  Beijing|2017|   200|
  * |  Beijing|null|   300|
  * |     null|null|   550| &lt;-- 整个数据集的总计
  * +---------+----+------+
  */</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 2: 如果使用基础的 groupBy 如何实现效果?</dt>
                                            <dd>
                                                <div class="listingblock">
                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val cityAndYear = sales
  .groupBy("city", "year") // 按照 city 和 year 聚合
  .agg(sum("amount") as "amount")

val city = sales
  .groupBy("city") // 按照 city 进行聚合
  .agg(sum("amount") as "amount")
  .select($"city", lit(null) as "year", $"amount")

val all = sales
  .groupBy() // 全局聚合
  .agg(sum("amount") as "amount")
  .select(lit(null) as "city", lit(null) as "year", $"amount")

cityAndYear
  .union(city)
  .union(all)
  .sort($"city".desc_nulls_last, $"year".asc_nulls_last)
  .show()

/**
  * 统计结果:
  * +---------+----+------+
  * |     city|year|amount|
  * +---------+----+------+
  * | Shanghai|2015|    50|
  * | Shanghai|2016|   150|
  * | Shanghai|null|   200|
  * |Guangzhou|2017|    50|
  * |Guangzhou|null|    50|
  * |  Beijing|2016|   100|
  * |  Beijing|2017|   200|
  * |  Beijing|null|   300|
  * |     null|null|   550|
  * +---------+----+------+
  */</code></pre>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                    <div class="paragraph">
                                        <p>很明显可以看到, 在上述案例中, <code>rollup</code> 就相当于先按照 <code>city</code>,
                                            <code>year</code> 进行聚合, 后按照 <code>city</code> 进行聚合, 最后对整个数据集进行聚合, 在按照 <code>city</code>
                                            聚合时, <code>year</code> 列值为 <code>null</code>, 聚合整个数据集的时候, 除了聚合列, 其它列值都为
                                            <code>null</code></p>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">使用 <code>rollup</code> 完成 <code>pm</code> 值的统计</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>上面的案例使用 <code>rollup</code> 来实现会非常的简单</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import org.apache.spark.sql.functions._

pmFinal.rollup('source, 'year)
  .agg(sum("pm") as "pm_total")
  .sort('source.asc_nulls_last, 'year.asc_nulls_last)
  .show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>cube</code></dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>cube</code> 的功能和 <code>rollup</code> 是一样的, 但也有区别, 区别如下</p>
                                    </div>
                                    <div class="ulist">
                                        <ul>
                                            <li>
                                                <p><code>rollup(A, B).sum&#169;</code></p>
                                                <div class="openblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>其结果集中会有三种数据形式: <code>A B C</code>, <code>A null C</code>,
                                                                <code>null null C</code></p>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>不知道大家发现没, 结果集中没有对 <code>B</code> 列的聚合结果</p>
                                                        </div>
                                                    </div>
                                                </div>
                                            </li>
                                            <li>
                                                <p><code>cube(A, B).sum&#169;</code></p>
                                                <div class="openblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>其结果集中会有四种数据形式: <code>A B C</code>, <code>A null C</code>,
                                                                <code>null null C</code>, <code>null B C</code></p>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>不知道大家发现没, 比 <code>rollup</code> 的结果集中多了一个 <code>null B
                                                                C</code>, 也就是说, <code>rollup</code> 只会按照第一个列来进行组合聚合, 但是
                                                                <code>cube</code> 会将全部列组合聚合</p>
                                                        </div>
                                                    </div>
                                                </div>
                                            </li>
                                        </ul>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import org.apache.spark.sql.functions._

pmFinal.cube('source, 'year)
  .agg(sum("pm") as "pm_total")
  .sort('source.asc_nulls_last, 'year.asc_nulls_last)
  .show()

/**
  * 结果集为
  *
  * +-------+----+---------+
  * | source|year| pm_total|
  * +-------+----+---------+
  * | dongsi|2013| 735606.0|
  * | dongsi|2014| 745808.0|
  * | dongsi|2015| 752083.0|
  * | dongsi|null|2233497.0|
  * |us_post|2010| 841834.0|
  * |us_post|2011| 796016.0|
  * |us_post|2012| 750838.0|
  * |us_post|2013| 882649.0|
  * |us_post|2014| 846475.0|
  * |us_post|2015| 714515.0|
  * |us_post|null|4832327.0|
  * |   null|2010| 841834.0| &lt;-- 新增
  * |   null|2011| 796016.0| &lt;-- 新增
  * |   null|2012| 750838.0| &lt;-- 新增
  * |   null|2013|1618255.0| &lt;-- 新增
  * |   null|2014|1592283.0| &lt;-- 新增
  * |   null|2015|1466598.0| &lt;-- 新增
  * |   null|null|7065824.0|
  * +-------+----+---------+
  */</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>SparkSQL</code> 中支持的 <code>SQL</code> 语句实现 <code>cube</code> 功能</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p><code>SparkSQL</code> 支持 <code>GROUPING SETS</code> 语句, 可以随意排列组合空值分组聚合的顺序和组成,
                                            既可以实现 <code>cube</code> 也可以实现 <code>rollup</code> 的功能</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">pmFinal.createOrReplaceTempView("pm_final")
spark.sql(
  """
    |select source, year, sum(pm)
    |from pm_final
    |group by source, year
    |grouping sets((source, year), (source), (year), ())
    |order by source asc nulls last, year asc nulls last
  """.stripMargin)
  .show()</code></pre>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1"><code>RelationalGroupedDataset</code></dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>常见的 <code>RelationalGroupedDataset</code> 获取方式有三种</p>
                                    </div>
                                    <div class="ulist">
                                        <ul>
                                            <li>
                                                <p><code>groupBy</code></p>
                                            </li>
                                            <li>
                                                <p><code>rollup</code></p>
                                            </li>
                                            <li>
                                                <p><code>cube</code></p>
                                            </li>
                                        </ul>
                                    </div>
                                    <div class="paragraph">
                                        <p>无论通过任何一种方式获取了 <code>RelationalGroupedDataset</code> 对象, 其所表示的都是是一个被分组的 <code>DataFrame</code>,
                                            通过这个对象, 可以对数据集的分组结果进行聚合</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
                                            <pre class="highlightjs highlight"><code class="language-scala hljs"
                                                                                     data-lang="scala">val groupedDF: RelationalGroupedDataset = pmDF.groupBy('year)</code></pre>
                                        </div>
                                    </div>
                                    <div class="paragraph">
                                        <p>需要注意的是, <code>RelationalGroupedDataset</code> 并不是 <code>DataFrame</code>,
                                            所以其中并没有 <code>DataFrame</code> 的方法, 只有如下一些聚合相关的方法, 如下这些方法在调用过后会生成 <code>DataFrame</code>
                                            对象, 然后就可以再次使用 <code>DataFrame</code> 的算子进行操作了</p>
                                    </div>
                                    <table class="tableblock frame-all grid-all stretch">
                                        <colgroup>
                                            <col style="width: 50%;">
                                            <col style="width: 50%;">
                                        </colgroup>
                                        <thead>
                                        <tr>
                                            <th class="tableblock halign-left valign-top">操作符</th>
                                            <th class="tableblock halign-left valign-top">解释</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                        <tr>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>avg</code>
                                            </p></td>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock">求平均数</p>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>count</code>
                                            </p></td>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock">求总数</p>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>max</code>
                                            </p></td>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock">求极大值</p>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>min</code>
                                            </p></td>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock">求极小值</p>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>mean</code>
                                            </p></td>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock">求均数</p>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>sum</code>
                                            </p></td>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock">求和</p>
                                            </td>
                                        </tr>
                                        <tr>
                                            <td class="tableblock halign-left valign-top"><p class="tableblock"><code>agg</code>
                                            </p></td>
                                            <td class="tableblock halign-left valign-top">
                                                <div class="content">
                                                    <div class="paragraph">
                                                        <p>聚合, 可以使用 <code>sql.functions</code> 中的函数来配合进行操作</p>
                                                    </div>
                                                    <div class="listingblock">
                                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">pmDF.groupBy('year)
    .agg(avg('pm) as "pm_avg")</code></pre>
                                                        </div>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                        </dd>
                    </dl>
                </div>
            </div>
        </div>
        <div class="sect1">
            <h2 id="_11_连接">11. 连接</h2>
            <div class="sectionbody">
                <div class="exampleblock">
                    <div class="title">导读</div>
                    <div class="content">
                        <div class="olist arabic">
                            <ol class="arabic">
                                <li>
                                    <p>无类型连接 <code>join</code></p>
                                </li>
                                <li>
                                    <p>连接类型 <code>Join Types</code></p>
                                </li>
                            </ol>
                        </div>
                    </div>
                </div>
                <div class="dlist">
                    <dl>
                        <dt class="hdlist1">无类型连接算子 <code>join</code> 的 <code>API</code></dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">Step 1: 什么是连接</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>按照 PostgreSQL 的文档中所说, 只要能在一个查询中, 同一时间并发的访问多条数据,
                                                                就叫做连接.</p>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>做到这件事有两种方式</p>
                                                        </div>
                                                        <div class="olist arabic">
                                                            <ol class="arabic">
                                                                <li>
                                                                    <p>一种是把两张表在逻辑上连接起来, 一条语句中同时访问两张表</p>
                                                                    <div class="listingblock">
                                                                        <div class="content">
                                                                            <pre class="highlightjs highlight"><code
                                                                                    class="language-sql hljs"
                                                                                    data-lang="sql">select * from user join address on user.address_id = address.id</code></pre>
                                                                        </div>
                                                                    </div>
                                                                </li>
                                                                <li>
                                                                    <p>还有一种方式就是表连接自己, 一条语句也能访问自己中的多条数据</p>
                                                                    <div class="listingblock">
                                                                        <div class="content">
                                                                            <pre class="highlightjs highlight"><code
                                                                                    class="language-sql hljs"
                                                                                    data-lang="sql">select * from user u1 join (select * from user) u2 on u1.id = u2.id</code></pre>
                                                                        </div>
                                                                    </div>
                                                                </li>
                                                            </ol>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 2: <code>join</code> 算子的使用非常简单, 大致的调用方式如下</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="listingblock">
                                                            <div class="content">
                                                                <pre class="highlightjs highlight"><code
                                                                        class="language-scala hljs" data-lang="scala">join(right: Dataset[_], joinExprs: Column, joinType: String): DataFrame</code></pre>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 3: 简单连接案例</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>表结构如下</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-text hljs" data-lang="text">+---+------+------+            +---+---------+
| id|  name|cityId|            | id|     name|
+---+------+------+            +---+---------+
|  0|  Lucy|     0|            |  0|  Beijing|
|  1|  Lily|     0|            |  1| Shanghai|
|  2|   Tim|     2|            |  2|Guangzhou|
|  3|Danial|     0|            +---+---------+
+---+------+------+</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>如果希望对这两张表进行连接, 首先应该注意的是可以连接的字段, 比如说此处的左侧表
                                                                <code>cityId</code> 和右侧表 <code>id</code> 就是可以连接的字段, 使用
                                                                <code>join</code> 算子就可以将两个表连接起来, 进行统一的查询</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val person = Seq((0, "Lucy", 0), (1, "Lily", 0), (2, "Tim", 2), (3, "Danial", 0))
  .toDF("id", "name", "cityId")

val cities = Seq((0, "Beijing"), (1, "Shanghai"), (2, "Guangzhou"))
  .toDF("id", "name")

person.join(cities, person.col("cityId") === cities.col("id"))
  .select(person.col("id"),
    person.col("name"),
    cities.col("name") as "city")
  .show()

/**
  * 执行结果:
  *
  * +---+------+---------+
  * | id|  name|     city|
  * +---+------+---------+
  * |  0|  Lucy|  Beijing|
  * |  1|  Lily|  Beijing|
  * |  2|   Tim|Guangzhou|
  * |  3|Danial|  Beijing|
  * +---+------+---------+
  */</code></pre>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 4: 什么是连接?</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>现在两个表连接得到了如下的表</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-text hljs" data-lang="text">+---+------+---------+
| id|  name|     city|
+---+------+---------+
|  0|  Lucy|  Beijing|
|  1|  Lily|  Beijing|
|  2|   Tim|Guangzhou|
|  3|Danial|  Beijing|
+---+------+---------+</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>通过对这张表的查询, 这个查询是作用于两张表的, 所以是同一时间访问了多条数据</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">spark.sql("select name from user_city where city = 'Beijing'").show()

/**
  * 执行结果
  *
  * +------+
  * |  name|
  * +------+
  * |  Lucy|
  * |  Lily|
  * |Danial|
  * +------+
  */</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="imageblock">
                                                            <div class="content">
                                                                <img alt="20190529095232"
                                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529095232.png">
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">连接类型</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="paragraph">
                                        <p>如果要运行如下代码, 需要先进行数据准备</p>
                                    </div>
                                    <div class="listingblock">
                                        <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">private val spark = SparkSession.builder()
  .master("local[6]")
  .appName("aggregation")
  .getOrCreate()

import spark.implicits._

val person = Seq((0, "Lucy", 0), (1, "Lily", 0), (2, "Tim", 2), (3, "Danial", 3))
  .toDF("id", "name", "cityId")
person.createOrReplaceTempView("person")

val cities = Seq((0, "Beijing"), (1, "Shanghai"), (2, "Guangzhou"))
  .toDF("id", "name")
cities.createOrReplaceTempView("cities")</code></pre>
                                        </div>
                                    </div>
                                    <table class="tableblock frame-all grid-all stretch">
                                        <colgroup>
                                            <col style="width: 15%;">
                                            <col style="width: 15%;">
                                            <col>
                                        </colgroup>
                                        <thead>
                                        <tr>
                                            <th class="tableblock halign-left valign-top">连接类型</th>
                                            <th class="tableblock halign-left valign-top">类型字段</th>
                                            <th class="tableblock halign-left valign-top">解释</th>
                                        </tr>
                                        </thead>
                                        <tbody>
                                        <tr>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock">交叉连接</p>
                                            </th>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>cross</code>
                                            </p></th>
                                            <td class="tableblock halign-left valign-top">
                                                <div class="content">
                                                    <div class="dlist">
                                                        <dl>
                                                            <dt class="hdlist1">解释</dt>
                                                            <dd>
                                                                <div class="paragraph">
                                                                    <p>交叉连接就是笛卡尔积, 就是两个表中所有的数据两两结对</p>
                                                                </div>
                                                                <div class="paragraph">
                                                                    <p>交叉连接是一个非常重的操作, 在生产中, 尽量不要将两个大数据集交叉连接, 如果一定要交叉连接,
                                                                        也需要在交叉连接后进行过滤, 优化器会进行优化</p>
                                                                </div>
                                                                <div class="imageblock">
                                                                    <div class="content">
                                                                        <img alt="20190529120732"
                                                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529120732.png" width="800">
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>SQL</code> 语句</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
                                                                        <pre class="highlightjs highlight"><code
                                                                                class="language-sql hljs"
                                                                                data-lang="sql">select * from person cross join cities</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>Dataset</code> 操作</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">person.crossJoin(cities)
  .where(person.col("cityId") === cities.col("id"))
  .show()</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                        </dl>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                        <tr>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock">内连接</p>
                                            </th>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>inner</code>
                                            </p></th>
                                            <td class="tableblock halign-left valign-top">
                                                <div class="content">
                                                    <div class="dlist">
                                                        <dl>
                                                            <dt class="hdlist1">解释</dt>
                                                            <dd>
                                                                <div class="paragraph">
                                                                    <p>内连接就是按照条件找到两个数据集关联的数据, 并且在生成的结果集中只存在能关联到的数据</p>
                                                                </div>
                                                                <div class="imageblock">
                                                                    <div class="content">
                                                                        <img alt="20190529115831"
                                                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529115831.png" width="800">
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>SQL</code> 语句</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
                                                                        <pre class="highlightjs highlight"><code
                                                                                class="language-sql hljs"
                                                                                data-lang="sql">select * from person inner join cities on person.cityId = cities.id</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>Dataset</code> 操作</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">person.join(right = cities,
  joinExprs = person("cityId") === cities("id"),
  joinType = "inner")
  .show()</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                        </dl>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                        <tr>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock">全外连接</p>
                                            </th>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>outer</code>,
                                                <code>full</code>, <code>fullouter</code></p></th>
                                            <td class="tableblock halign-left valign-top">
                                                <div class="content">
                                                    <div class="dlist">
                                                        <dl>
                                                            <dt class="hdlist1">解释</dt>
                                                            <dd>
                                                                <div class="paragraph">
                                                                    <p>内连接和外连接的最大区别, 就是内连接的结果集中只有可以连接上的数据,
                                                                        而外连接可以包含没有连接上的数据, 根据情况的不同, 外连接又可以分为很多种,
                                                                        比如所有的没连接上的数据都放入结果集, 就叫做全外连接</p>
                                                                </div>
                                                                <div class="imageblock">
                                                                    <div class="content">
                                                                        <img alt="20190529120033"
                                                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529120033.png" width="800">
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>SQL</code> 语句</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
                                                                        <pre class="highlightjs highlight"><code
                                                                                class="language-sql hljs"
                                                                                data-lang="sql">select * from person full outer join cities on person.cityId = cities.id</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>Dataset</code> 操作</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">person.join(right = cities,
  joinExprs = person("cityId") === cities("id"),
  joinType = "full") // "outer", "full", "full_outer"
  .show()</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                        </dl>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                        <tr>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock">左外连接</p>
                                            </th>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>leftouter</code>,
                                                <code>left</code></p></th>
                                            <td class="tableblock halign-left valign-top">
                                                <div class="content">
                                                    <div class="dlist">
                                                        <dl>
                                                            <dt class="hdlist1">解释</dt>
                                                            <dd>
                                                                <div class="paragraph">
                                                                    <p>左外连接是全外连接的一个子集, 全外连接中包含左右两边数据集没有连接上的数据,
                                                                        而左外连接只包含左边数据集中没有连接上的数据</p>
                                                                </div>
                                                                <div class="imageblock">
                                                                    <div class="content">
                                                                        <img alt="20190529120139"
                                                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529120139.png" width="500">
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>SQL</code> 语句</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
                                                                        <pre class="highlightjs highlight"><code
                                                                                class="language-sql hljs"
                                                                                data-lang="sql">select * from person left join cities on person.cityId = cities.id</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>Dataset</code> 操作</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">person.join(right = cities,
  joinExprs = person("cityId") === cities("id"),
  joinType = "left") // leftouter, left
  .show()</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                        </dl>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                        <tr>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>LeftAnti</code>
                                            </p></th>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>leftanti</code>
                                            </p></th>
                                            <td class="tableblock halign-left valign-top">
                                                <div class="content">
                                                    <div class="dlist">
                                                        <dl>
                                                            <dt class="hdlist1">解释</dt>
                                                            <dd>
                                                                <div class="paragraph">
                                                                    <p><code>LeftAnti</code> 是一种特殊的连接形式, 和左外连接类似,
                                                                        但是其结果集中没有右侧的数据, 只包含左边集合中没连接上的数据</p>
                                                                </div>
                                                                <div class="imageblock">
                                                                    <div class="content">
                                                                        <img alt="20190529120454"
                                                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529120454.png" width="800">
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>SQL</code> 语句</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
                                                                        <pre class="highlightjs highlight"><code
                                                                                class="language-sql hljs"
                                                                                data-lang="sql">select * from person left anti join cities on person.cityId = cities.id</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>Dataset</code> 操作</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">person.join(right = cities,
  joinExprs = person("cityId") === cities("id"),
  joinType = "left_anti")
  .show()</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                        </dl>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                        <tr>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>LeftSemi</code>
                                            </p></th>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>leftsemi</code>
                                            </p></th>
                                            <td class="tableblock halign-left valign-top">
                                                <div class="content">
                                                    <div class="dlist">
                                                        <dl>
                                                            <dt class="hdlist1">解释</dt>
                                                            <dd>
                                                                <div class="paragraph">
                                                                    <p>和 <code>LeftAnti</code> 恰好相反,
                                                                        <code>LeftSemi</code> 的结果集也没有右侧集合的数据,
                                                                        但是只包含左侧集合中连接上的数据</p>
                                                                </div>
                                                                <div class="imageblock">
                                                                    <div class="content">
                                                                        <img alt="20190529120406"
                                                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529120406.png" width="800">
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>SQL</code> 语句</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
                                                                        <pre class="highlightjs highlight"><code
                                                                                class="language-sql hljs"
                                                                                data-lang="sql">select * from person left semi join cities on person.cityId = cities.id</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>Dataset</code> 操作</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">person.join(right = cities,
  joinExprs = person("cityId") === cities("id"),
  joinType = "left_semi")
  .show()</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                        </dl>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                        <tr>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock">右外连接</p>
                                            </th>
                                            <th class="tableblock halign-left valign-top"><p class="tableblock"><code>rightouter</code>,
                                                <code>right</code></p></th>
                                            <td class="tableblock halign-left valign-top">
                                                <div class="content">
                                                    <div class="dlist">
                                                        <dl>
                                                            <dt class="hdlist1">解释</dt>
                                                            <dd>
                                                                <div class="paragraph">
                                                                    <p>右外连接和左外连接刚好相反, 左外是包含左侧未连接的数据, 和两个数据集中连接上的数据,
                                                                        而右外是包含右侧未连接的数据, 和两个数据集中连接上的数据</p>
                                                                </div>
                                                                <div class="imageblock">
                                                                    <div class="content">
                                                                        <img alt="20190529120222"
                                                                             src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529120222.png" width="800">
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>SQL</code> 语句</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
                                                                        <pre class="highlightjs highlight"><code
                                                                                class="language-sql hljs"
                                                                                data-lang="sql">select * from person right join cities on person.cityId = cities.id</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                            <dt class="hdlist1"><code>Dataset</code> 操作</dt>
                                                            <dd>
                                                                <div class="listingblock">
                                                                    <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">person.join(right = cities,
  joinExprs = person("cityId") === cities("id"),
  joinType = "right") // rightouter, right
  .show()</code></pre>
                                                                    </div>
                                                                </div>
                                                            </dd>
                                                        </dl>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                        </dd>
                        <dt class="hdlist1">[扩展] 广播连接</dt>
                        <dd>
                            <div class="sidebarblock">
                                <div class="content">
                                    <div class="dlist">
                                        <dl>
                                            <dt class="hdlist1">Step 1: 正常情况下的 <code>Join</code> 过程</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="imageblock">
                                                            <div class="content">
                                                                <img alt="20190529151419"
                                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529151419.png">
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p><code>Join</code> 会在集群中分发两个数据集, 两个数据集都要复制到
                                                                <code>Reducer</code> 端, 是一个非常复杂和标准的 <code>ShuffleDependency</code>,
                                                                有什么可以优化效率吗?</p>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 2: <code>Map</code> 端 <code>Join</code></dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>前面图中看的过程, 之所以说它效率很低, 原因是需要在集群中进行数据拷贝, 如果能减少数据拷贝,
                                                                就能减少开销</p>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>如果能够只分发一个较小的数据集呢?</p>
                                                        </div>
                                                        <div class="imageblock">
                                                            <div class="content">
                                                                <img alt="20190529152206"
                                                                     src="https://doc-1256053707.cos.ap-beijing.myqcloud.com/20190529152206.png">
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>可以将小数据集收集起来, 分发给每一个 <code>Executor</code>, 然后在需要 <code>Join</code>
                                                                的时候, 让较大的数据集在 <code>Map</code> 端直接获取小数据集, 从而进行 <code>Join</code>,
                                                                这种方式是不需要进行 <code>Shuffle</code> 的, 所以称之为
                                                                <code>Map</code> 端 <code>Join</code></p>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 3: <code>Map</code> 端 <code>Join</code> 的常规实现</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>如果使用 <code>RDD</code> 的话, 该如何实现 <code>Map</code> 端 <code>Join</code>
                                                                呢?</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">val personRDD = spark.sparkContext.parallelize(Seq((0, "Lucy", 0),
  (1, "Lily", 0), (2, "Tim", 2), (3, "Danial", 3)))

val citiesRDD = spark.sparkContext.parallelize(Seq((0, "Beijing"),
  (1, "Shanghai"), (2, "Guangzhou")))

val citiesBroadcast = spark.sparkContext.broadcast(citiesRDD.collectAsMap())

val result = personRDD.mapPartitions(
  iter =&gt; {
    val citiesMap = citiesBroadcast.value
    // 使用列表生成式 yield 生成列表
    val result = for (person &lt;- iter if citiesMap.contains(person._3))
      yield (person._1, person._2, citiesMap(person._3))
    result
  }
).collect()

result.foreach(println(_))</code></pre>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 4: 使用 <code>Dataset</code> 实现 <code>Join</code>
                                                的时候会自动进行 <code>Map</code> 端 <code>Join</code></dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>自动进行 <code>Map</code> 端 <code>Join</code> 需要依赖一个系统参数
                                                                <code>spark.sql.autoBroadcastJoinThreshold</code>,
                                                                当数据集小于这个参数的大小时, 会自动进行 <code>Map</code> 端
                                                                <code>Join</code></p>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>如下, 开启自动 <code>Join</code></p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">println(spark.conf.get("spark.sql.autoBroadcastJoinThreshold").toInt / 1024 / 1024)

println(person.crossJoin(cities).queryExecution.sparkPlan.numberedTreeString)</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>当关闭这个参数的时候, 则不会自动 Map 端 Join 了</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">spark.conf.set("spark.sql.autoBroadcastJoinThreshold", -1)
println(person.crossJoin(cities).queryExecution.sparkPlan.numberedTreeString)</code></pre>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                            <dt class="hdlist1">Step 5: 也可以使用函数强制开启 Map 端 Join</dt>
                                            <dd>
                                                <div class="exampleblock">
                                                    <div class="content">
                                                        <div class="paragraph">
                                                            <p>在使用 Dataset 的 join 时, 可以使用 broadcast 函数来实现 Map 端 Join</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">import org.apache.spark.sql.functions._
spark.conf.set("spark.sql.autoBroadcastJoinThreshold", -1)
println(person.crossJoin(broadcast(cities)).queryExecution.sparkPlan.numberedTreeString)</code></pre>
                                                            </div>
                                                        </div>
                                                        <div class="paragraph">
                                                            <p>即使是使用 SQL 也可以使用特殊的语法开启</p>
                                                        </div>
                                                        <div class="listingblock">
                                                            <div class="content">
<pre class="highlightjs highlight"><code class="language-scala hljs" data-lang="scala">spark.conf.set("spark.sql.autoBroadcastJoinThreshold", -1)
val resultDF = spark.sql(
  """
    |select /*+ MAPJOIN (rt) */ * from person cross join cities rt
  """.stripMargin)
println(resultDF.queryExecution.sparkPlan.numberedTreeString)</code></pre>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </div>
                                            </dd>
                                        </dl>
                                    </div>
                                </div>
                            </div>
                        </dd>
                    </dl>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>
